| 1 | /**************************************************************************/ |
| 2 | /* scene_replication_interface.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 "scene_replication_interface.h" |
| 32 | |
| 33 | #include "scene_multiplayer.h" |
| 34 | |
| 35 | #include "core/debugger/engine_debugger.h" |
| 36 | #include "core/io/marshalls.h" |
| 37 | #include "scene/main/node.h" |
| 38 | #include "scene/scene_string_names.h" |
| 39 | |
| 40 | #define MAKE_ROOM(m_amount) \ |
| 41 | if (packet_cache.size() < m_amount) \ |
| 42 | packet_cache.resize(m_amount); |
| 43 | |
| 44 | #ifdef DEBUG_ENABLED |
| 45 | _FORCE_INLINE_ void SceneReplicationInterface::_profile_node_data(const String &p_what, ObjectID p_id, int p_size) { |
| 46 | if (EngineDebugger::is_profiling("multiplayer:replication" )) { |
| 47 | Array values; |
| 48 | values.push_back(p_what); |
| 49 | values.push_back(p_id); |
| 50 | values.push_back(p_size); |
| 51 | EngineDebugger::profiler_add_frame_data("multiplayer:replication" , values); |
| 52 | } |
| 53 | } |
| 54 | #endif |
| 55 | |
| 56 | SceneReplicationInterface::TrackedNode &SceneReplicationInterface::_track(const ObjectID &p_id) { |
| 57 | if (!tracked_nodes.has(p_id)) { |
| 58 | tracked_nodes[p_id] = TrackedNode(p_id); |
| 59 | Node *node = get_id_as<Node>(p_id); |
| 60 | node->connect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationInterface::_untrack).bind(p_id), Node::CONNECT_ONE_SHOT); |
| 61 | } |
| 62 | return tracked_nodes[p_id]; |
| 63 | } |
| 64 | |
| 65 | void SceneReplicationInterface::_untrack(const ObjectID &p_id) { |
| 66 | if (!tracked_nodes.has(p_id)) { |
| 67 | return; |
| 68 | } |
| 69 | uint32_t net_id = tracked_nodes[p_id].net_id; |
| 70 | uint32_t peer = tracked_nodes[p_id].remote_peer; |
| 71 | tracked_nodes.erase(p_id); |
| 72 | // If it was spawned by a remote, remove it from the received nodes. |
| 73 | if (peer && peers_info.has(peer)) { |
| 74 | peers_info[peer].recv_nodes.erase(net_id); |
| 75 | } |
| 76 | // If we spawned or synced it, we need to remove it from any peer it was sent to. |
| 77 | if (net_id || peer == 0) { |
| 78 | for (KeyValue<int, PeerInfo> &E : peers_info) { |
| 79 | E.value.spawn_nodes.erase(p_id); |
| 80 | } |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | void SceneReplicationInterface::_free_remotes(const PeerInfo &p_info) { |
| 85 | for (const KeyValue<uint32_t, ObjectID> &E : p_info.recv_nodes) { |
| 86 | Node *node = tracked_nodes.has(E.value) ? get_id_as<Node>(E.value) : nullptr; |
| 87 | ERR_CONTINUE(!node); |
| 88 | node->queue_free(); |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | void SceneReplicationInterface::on_peer_change(int p_id, bool p_connected) { |
| 93 | if (p_connected) { |
| 94 | peers_info[p_id] = PeerInfo(); |
| 95 | for (const ObjectID &oid : spawned_nodes) { |
| 96 | _update_spawn_visibility(p_id, oid); |
| 97 | } |
| 98 | for (const ObjectID &oid : sync_nodes) { |
| 99 | _update_sync_visibility(p_id, get_id_as<MultiplayerSynchronizer>(oid)); |
| 100 | } |
| 101 | } else { |
| 102 | ERR_FAIL_COND(!peers_info.has(p_id)); |
| 103 | _free_remotes(peers_info[p_id]); |
| 104 | peers_info.erase(p_id); |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | void SceneReplicationInterface::on_reset() { |
| 109 | for (const KeyValue<int, PeerInfo> &E : peers_info) { |
| 110 | _free_remotes(E.value); |
| 111 | } |
| 112 | peers_info.clear(); |
| 113 | // Tracked nodes are cleared on deletion, here we only reset the ids so they can be later re-assigned. |
| 114 | for (KeyValue<ObjectID, TrackedNode> &E : tracked_nodes) { |
| 115 | TrackedNode &tobj = E.value; |
| 116 | tobj.net_id = 0; |
| 117 | tobj.remote_peer = 0; |
| 118 | } |
| 119 | for (const ObjectID &oid : sync_nodes) { |
| 120 | MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(oid); |
| 121 | ERR_CONTINUE(!sync); |
| 122 | sync->reset(); |
| 123 | } |
| 124 | last_net_id = 0; |
| 125 | } |
| 126 | |
| 127 | void SceneReplicationInterface::on_network_process() { |
| 128 | // Prevent endless stalling in case of unforeseen spawn errors. |
| 129 | if (spawn_queue.size()) { |
| 130 | ERR_PRINT("An error happened during last spawn, this usually means the 'ready' signal was not emitted by the spawned node." ); |
| 131 | for (const ObjectID &oid : spawn_queue) { |
| 132 | Node *node = get_id_as<Node>(oid); |
| 133 | ERR_CONTINUE(!node); |
| 134 | if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready))) { |
| 135 | node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready)); |
| 136 | } |
| 137 | } |
| 138 | spawn_queue.clear(); |
| 139 | } |
| 140 | |
| 141 | // Process syncs. |
| 142 | uint64_t usec = OS::get_singleton()->get_ticks_usec(); |
| 143 | for (KeyValue<int, PeerInfo> &E : peers_info) { |
| 144 | const HashSet<ObjectID> to_sync = E.value.sync_nodes; |
| 145 | if (to_sync.is_empty()) { |
| 146 | continue; // Nothing to sync |
| 147 | } |
| 148 | uint16_t sync_net_time = ++E.value.last_sent_sync; |
| 149 | _send_sync(E.key, to_sync, sync_net_time, usec); |
| 150 | _send_delta(E.key, to_sync, usec, E.value.last_watch_usecs); |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) { |
| 155 | Node *node = Object::cast_to<Node>(p_obj); |
| 156 | ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER); |
| 157 | MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object()); |
| 158 | ERR_FAIL_COND_V(!spawner, ERR_INVALID_PARAMETER); |
| 159 | // Track node. |
| 160 | const ObjectID oid = node->get_instance_id(); |
| 161 | TrackedNode &tobj = _track(oid); |
| 162 | |
| 163 | // Spawn state needs to be callected after "ready", but the spawn order follows "enter_tree". |
| 164 | ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE); |
| 165 | tobj.spawner = spawner->get_instance_id(); |
| 166 | spawn_queue.insert(oid); |
| 167 | node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready).bind(oid), Node::CONNECT_ONE_SHOT); |
| 168 | return OK; |
| 169 | } |
| 170 | |
| 171 | void SceneReplicationInterface::_node_ready(const ObjectID &p_oid) { |
| 172 | ERR_FAIL_COND(!spawn_queue.has(p_oid)); // Bug. |
| 173 | |
| 174 | // If we are a nested spawn, we need to wait until the parent is ready. |
| 175 | if (p_oid != *(spawn_queue.begin())) { |
| 176 | return; |
| 177 | } |
| 178 | |
| 179 | for (const ObjectID &oid : spawn_queue) { |
| 180 | ERR_CONTINUE(!tracked_nodes.has(oid)); |
| 181 | |
| 182 | TrackedNode &tobj = tracked_nodes[oid]; |
| 183 | MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tobj.spawner); |
| 184 | ERR_CONTINUE(!spawner); |
| 185 | |
| 186 | spawned_nodes.insert(oid); |
| 187 | if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) { |
| 188 | if (tobj.net_id == 0) { |
| 189 | tobj.net_id = ++last_net_id; |
| 190 | } |
| 191 | _update_spawn_visibility(0, oid); |
| 192 | } |
| 193 | } |
| 194 | spawn_queue.clear(); |
| 195 | } |
| 196 | |
| 197 | Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) { |
| 198 | Node *node = Object::cast_to<Node>(p_obj); |
| 199 | ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER); |
| 200 | MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object()); |
| 201 | ERR_FAIL_COND_V(!p_obj || !spawner, ERR_INVALID_PARAMETER); |
| 202 | // Forcibly despawn to all peers that knowns me. |
| 203 | int len = 0; |
| 204 | Error err = _make_despawn_packet(node, len); |
| 205 | ERR_FAIL_COND_V(err != OK, ERR_BUG); |
| 206 | const ObjectID oid = p_obj->get_instance_id(); |
| 207 | for (const KeyValue<int, PeerInfo> &E : peers_info) { |
| 208 | if (!E.value.spawn_nodes.has(oid)) { |
| 209 | continue; |
| 210 | } |
| 211 | _send_raw(packet_cache.ptr(), len, E.key, true); |
| 212 | } |
| 213 | // Also remove spawner tracking from the replication state. |
| 214 | ERR_FAIL_COND_V(!tracked_nodes.has(oid), ERR_INVALID_PARAMETER); |
| 215 | TrackedNode &tobj = _track(oid); |
| 216 | ERR_FAIL_COND_V(tobj.spawner != spawner->get_instance_id(), ERR_INVALID_PARAMETER); |
| 217 | tobj.spawner = ObjectID(); |
| 218 | spawned_nodes.erase(oid); |
| 219 | for (KeyValue<int, PeerInfo> &E : peers_info) { |
| 220 | E.value.spawn_nodes.erase(oid); |
| 221 | } |
| 222 | return OK; |
| 223 | } |
| 224 | |
| 225 | Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_config) { |
| 226 | Node *node = Object::cast_to<Node>(p_obj); |
| 227 | ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER); |
| 228 | MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object()); |
| 229 | ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER); |
| 230 | |
| 231 | // Add to synchronizer list. |
| 232 | TrackedNode &tobj = _track(p_obj->get_instance_id()); |
| 233 | const ObjectID sid = sync->get_instance_id(); |
| 234 | tobj.synchronizers.insert(sid); |
| 235 | sync_nodes.insert(sid); |
| 236 | |
| 237 | // Update visibility. |
| 238 | sync->connect("visibility_changed" , callable_mp(this, &SceneReplicationInterface::_visibility_changed).bind(sync->get_instance_id())); |
| 239 | _update_sync_visibility(0, sync); |
| 240 | |
| 241 | if (pending_spawn == p_obj->get_instance_id() && sync->get_multiplayer_authority() == pending_spawn_remote) { |
| 242 | // Try to apply synchronizer Net ID |
| 243 | ERR_FAIL_COND_V_MSG(pending_sync_net_ids.is_empty(), ERR_INVALID_DATA, vformat("The MultiplayerSynchronizer at path \"%s\" is unable to process the pending spawn since it has no network ID. This might happen when changing the multiplayer authority during the \"_ready\" callback. Make sure to only change the authority of multiplayer synchronizers during \"_enter_tree\" or the \"_spawn_custom\" callback of their multiplayer spawner." , sync->get_path())); |
| 244 | ERR_FAIL_COND_V(!peers_info.has(pending_spawn_remote), ERR_INVALID_DATA); |
| 245 | uint32_t net_id = pending_sync_net_ids[0]; |
| 246 | pending_sync_net_ids.pop_front(); |
| 247 | peers_info[pending_spawn_remote].recv_sync_ids[net_id] = sync->get_instance_id(); |
| 248 | |
| 249 | // Try to apply spawn state (before ready). |
| 250 | if (pending_buffer_size > 0) { |
| 251 | ERR_FAIL_COND_V(!node || sync->get_replication_config().is_null(), ERR_UNCONFIGURED); |
| 252 | int consumed = 0; |
| 253 | const List<NodePath> props = sync->get_replication_config()->get_spawn_properties(); |
| 254 | Vector<Variant> vars; |
| 255 | vars.resize(props.size()); |
| 256 | Error err = MultiplayerAPI::decode_and_decompress_variants(vars, pending_buffer, pending_buffer_size, consumed); |
| 257 | ERR_FAIL_COND_V(err, err); |
| 258 | if (consumed > 0) { |
| 259 | pending_buffer += consumed; |
| 260 | pending_buffer_size -= consumed; |
| 261 | err = MultiplayerSynchronizer::set_state(props, node, vars); |
| 262 | ERR_FAIL_COND_V(err, err); |
| 263 | } |
| 264 | } |
| 265 | } |
| 266 | return OK; |
| 267 | } |
| 268 | |
| 269 | Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_config) { |
| 270 | Node *node = Object::cast_to<Node>(p_obj); |
| 271 | ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER); |
| 272 | MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object()); |
| 273 | ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER); |
| 274 | sync->disconnect("visibility_changed" , callable_mp(this, &SceneReplicationInterface::_visibility_changed)); |
| 275 | // Untrack synchronizer. |
| 276 | const ObjectID oid = node->get_instance_id(); |
| 277 | const ObjectID sid = sync->get_instance_id(); |
| 278 | ERR_FAIL_COND_V(!tracked_nodes.has(oid), ERR_INVALID_PARAMETER); |
| 279 | TrackedNode &tobj = _track(oid); |
| 280 | tobj.synchronizers.erase(sid); |
| 281 | sync_nodes.erase(sid); |
| 282 | for (KeyValue<int, PeerInfo> &E : peers_info) { |
| 283 | E.value.sync_nodes.erase(sid); |
| 284 | E.value.last_watch_usecs.erase(sid); |
| 285 | if (sync->get_net_id()) { |
| 286 | E.value.recv_sync_ids.erase(sync->get_net_id()); |
| 287 | } |
| 288 | } |
| 289 | return OK; |
| 290 | } |
| 291 | |
| 292 | void SceneReplicationInterface::_visibility_changed(int p_peer, ObjectID p_sid) { |
| 293 | MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(p_sid); |
| 294 | ERR_FAIL_COND(!sync); // Bug. |
| 295 | Node *node = sync->get_root_node(); |
| 296 | ERR_FAIL_COND(!node); // Bug. |
| 297 | const ObjectID oid = node->get_instance_id(); |
| 298 | if (spawned_nodes.has(oid) && p_peer != multiplayer->get_unique_id()) { |
| 299 | _update_spawn_visibility(p_peer, oid); |
| 300 | } |
| 301 | _update_sync_visibility(p_peer, sync); |
| 302 | } |
| 303 | |
| 304 | bool SceneReplicationInterface::is_rpc_visible(const ObjectID &p_oid, int p_peer) const { |
| 305 | if (!tracked_nodes.has(p_oid)) { |
| 306 | return true; // Untracked nodes are always visible to RPCs. |
| 307 | } |
| 308 | ERR_FAIL_COND_V(p_peer < 0, false); |
| 309 | const TrackedNode &tnode = tracked_nodes[p_oid]; |
| 310 | if (tnode.synchronizers.is_empty()) { |
| 311 | return true; // No synchronizers means no visibility restrictions. |
| 312 | } |
| 313 | if (tnode.remote_peer && uint32_t(p_peer) == tnode.remote_peer) { |
| 314 | return true; // RPCs on spawned nodes are always visible to spawner. |
| 315 | } else if (spawned_nodes.has(p_oid)) { |
| 316 | // It's a spawned node we control, this can be fast. |
| 317 | if (p_peer) { |
| 318 | return peers_info.has(p_peer) && peers_info[p_peer].spawn_nodes.has(p_oid); |
| 319 | } else { |
| 320 | for (const KeyValue<int, PeerInfo> &E : peers_info) { |
| 321 | if (!E.value.spawn_nodes.has(p_oid)) { |
| 322 | return false; // Not public. |
| 323 | } |
| 324 | } |
| 325 | return true; // All peers have this node. |
| 326 | } |
| 327 | } else { |
| 328 | // Cycle object synchronizers to check visibility. |
| 329 | for (const ObjectID &sid : tnode.synchronizers) { |
| 330 | MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(sid); |
| 331 | ERR_CONTINUE(!sync); |
| 332 | // RPC visibility is composed using OR when multiple synchronizers are present. |
| 333 | // Note that we don't really care about authority here which may lead to unexpected |
| 334 | // results when using multiple synchronizers to control the same node. |
| 335 | if (sync->is_visible_to(p_peer)) { |
| 336 | return true; |
| 337 | } |
| 338 | } |
| 339 | return false; // Not visible. |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | Error SceneReplicationInterface::_update_sync_visibility(int p_peer, MultiplayerSynchronizer *p_sync) { |
| 344 | ERR_FAIL_COND_V(!p_sync, ERR_BUG); |
| 345 | if (!multiplayer->has_multiplayer_peer() || !p_sync->is_multiplayer_authority() || p_peer == multiplayer->get_unique_id()) { |
| 346 | return OK; |
| 347 | } |
| 348 | |
| 349 | const ObjectID &sid = p_sync->get_instance_id(); |
| 350 | bool is_visible = p_sync->is_visible_to(p_peer); |
| 351 | if (p_peer == 0) { |
| 352 | for (KeyValue<int, PeerInfo> &E : peers_info) { |
| 353 | // Might be visible to this specific peer. |
| 354 | bool is_visible_to_peer = is_visible || p_sync->is_visible_to(E.key); |
| 355 | if (is_visible_to_peer == E.value.sync_nodes.has(sid)) { |
| 356 | continue; |
| 357 | } |
| 358 | if (is_visible_to_peer) { |
| 359 | E.value.sync_nodes.insert(sid); |
| 360 | } else { |
| 361 | E.value.sync_nodes.erase(sid); |
| 362 | E.value.last_watch_usecs.erase(sid); |
| 363 | } |
| 364 | } |
| 365 | return OK; |
| 366 | } else { |
| 367 | ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER); |
| 368 | if (is_visible == peers_info[p_peer].sync_nodes.has(sid)) { |
| 369 | return OK; |
| 370 | } |
| 371 | if (is_visible) { |
| 372 | peers_info[p_peer].sync_nodes.insert(sid); |
| 373 | } else { |
| 374 | peers_info[p_peer].sync_nodes.erase(sid); |
| 375 | peers_info[p_peer].last_watch_usecs.erase(sid); |
| 376 | } |
| 377 | return OK; |
| 378 | } |
| 379 | } |
| 380 | |
| 381 | Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const ObjectID &p_oid) { |
| 382 | const TrackedNode *tnode = tracked_nodes.getptr(p_oid); |
| 383 | ERR_FAIL_COND_V(!tnode, ERR_BUG); |
| 384 | MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tnode->spawner); |
| 385 | Node *node = get_id_as<Node>(p_oid); |
| 386 | ERR_FAIL_COND_V(!node || !spawner || !spawner->is_multiplayer_authority(), ERR_BUG); |
| 387 | ERR_FAIL_COND_V(!tracked_nodes.has(p_oid), ERR_BUG); |
| 388 | const HashSet<ObjectID> synchronizers = tracked_nodes[p_oid].synchronizers; |
| 389 | bool is_visible = true; |
| 390 | for (const ObjectID &sid : synchronizers) { |
| 391 | MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(sid); |
| 392 | ERR_CONTINUE(!sync); |
| 393 | if (!sync->is_multiplayer_authority()) { |
| 394 | continue; |
| 395 | } |
| 396 | // Spawn visibility is composed using OR when multiple synchronizers are present. |
| 397 | if (sync->is_visible_to(p_peer)) { |
| 398 | is_visible = true; |
| 399 | break; |
| 400 | } |
| 401 | is_visible = false; |
| 402 | } |
| 403 | // Spawn (and despawn) when needed. |
| 404 | HashSet<int> to_spawn; |
| 405 | HashSet<int> to_despawn; |
| 406 | if (p_peer) { |
| 407 | ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER); |
| 408 | if (is_visible == peers_info[p_peer].spawn_nodes.has(p_oid)) { |
| 409 | return OK; |
| 410 | } |
| 411 | if (is_visible) { |
| 412 | to_spawn.insert(p_peer); |
| 413 | } else { |
| 414 | to_despawn.insert(p_peer); |
| 415 | } |
| 416 | } else { |
| 417 | // Check visibility for each peers. |
| 418 | for (const KeyValue<int, PeerInfo> &E : peers_info) { |
| 419 | if (is_visible) { |
| 420 | // This is fast, since the the object is visible to everyone, we don't need to check each peer. |
| 421 | if (E.value.spawn_nodes.has(p_oid)) { |
| 422 | // Already spawned. |
| 423 | continue; |
| 424 | } |
| 425 | to_spawn.insert(E.key); |
| 426 | } else { |
| 427 | // Need to check visibility for each peer. |
| 428 | _update_spawn_visibility(E.key, p_oid); |
| 429 | } |
| 430 | } |
| 431 | } |
| 432 | if (to_spawn.size()) { |
| 433 | int len = 0; |
| 434 | _make_spawn_packet(node, spawner, len); |
| 435 | for (int pid : to_spawn) { |
| 436 | ERR_CONTINUE(!peers_info.has(pid)); |
| 437 | int path_id; |
| 438 | multiplayer->get_path_cache()->send_object_cache(spawner, pid, path_id); |
| 439 | _send_raw(packet_cache.ptr(), len, pid, true); |
| 440 | peers_info[pid].spawn_nodes.insert(p_oid); |
| 441 | } |
| 442 | } |
| 443 | if (to_despawn.size()) { |
| 444 | int len = 0; |
| 445 | _make_despawn_packet(node, len); |
| 446 | for (int pid : to_despawn) { |
| 447 | ERR_CONTINUE(!peers_info.has(pid)); |
| 448 | peers_info[pid].spawn_nodes.erase(p_oid); |
| 449 | _send_raw(packet_cache.ptr(), len, pid, true); |
| 450 | } |
| 451 | } |
| 452 | return OK; |
| 453 | } |
| 454 | |
| 455 | Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable) { |
| 456 | ERR_FAIL_COND_V(!p_buffer || p_size < 1, ERR_INVALID_PARAMETER); |
| 457 | ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED); |
| 458 | |
| 459 | Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); |
| 460 | peer->set_transfer_channel(0); |
| 461 | peer->set_transfer_mode(p_reliable ? MultiplayerPeer::TRANSFER_MODE_RELIABLE : MultiplayerPeer::TRANSFER_MODE_UNRELIABLE); |
| 462 | return multiplayer->send_command(p_peer, p_buffer, p_size); |
| 463 | } |
| 464 | |
| 465 | Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len) { |
| 466 | ERR_FAIL_COND_V(!multiplayer || !p_node || !p_spawner, ERR_BUG); |
| 467 | |
| 468 | const ObjectID oid = p_node->get_instance_id(); |
| 469 | const TrackedNode *tnode = tracked_nodes.getptr(oid); |
| 470 | ERR_FAIL_COND_V(!tnode, ERR_INVALID_PARAMETER); |
| 471 | |
| 472 | uint32_t nid = tnode->net_id; |
| 473 | ERR_FAIL_COND_V(!nid, ERR_UNCONFIGURED); |
| 474 | |
| 475 | // Prepare custom arg and scene_id |
| 476 | uint8_t scene_id = p_spawner->find_spawnable_scene_index_from_object(oid); |
| 477 | bool is_custom = scene_id == MultiplayerSpawner::INVALID_ID; |
| 478 | Variant spawn_arg = p_spawner->get_spawn_argument(oid); |
| 479 | int spawn_arg_size = 0; |
| 480 | if (is_custom) { |
| 481 | Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, nullptr, spawn_arg_size, false); |
| 482 | ERR_FAIL_COND_V(err, err); |
| 483 | } |
| 484 | |
| 485 | // Prepare spawn state. |
| 486 | List<NodePath> state_props; |
| 487 | List<uint32_t> sync_ids; |
| 488 | const HashSet<ObjectID> synchronizers = tnode->synchronizers; |
| 489 | for (const ObjectID &sid : synchronizers) { |
| 490 | MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(sid); |
| 491 | if (!sync->is_multiplayer_authority()) { |
| 492 | continue; |
| 493 | } |
| 494 | ERR_CONTINUE(!sync); |
| 495 | ERR_FAIL_COND_V(sync->get_replication_config().is_null(), ERR_BUG); |
| 496 | for (const NodePath &prop : sync->get_replication_config()->get_spawn_properties()) { |
| 497 | state_props.push_back(prop); |
| 498 | } |
| 499 | // Ensure the synchronizer has an ID. |
| 500 | if (sync->get_net_id() == 0) { |
| 501 | sync->set_net_id(++last_net_id); |
| 502 | } |
| 503 | sync_ids.push_back(sync->get_net_id()); |
| 504 | } |
| 505 | int state_size = 0; |
| 506 | Vector<Variant> state_vars; |
| 507 | Vector<const Variant *> state_varp; |
| 508 | if (state_props.size()) { |
| 509 | Error err = MultiplayerSynchronizer::get_state(state_props, p_node, state_vars, state_varp); |
| 510 | ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to retrieve spawn state." ); |
| 511 | err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), nullptr, state_size); |
| 512 | ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to encode spawn state." ); |
| 513 | } |
| 514 | |
| 515 | // Encode scene ID, path ID, net ID, node name. |
| 516 | int path_id = multiplayer->get_path_cache()->make_object_cache(p_spawner); |
| 517 | CharString cname = p_node->get_name().operator String().utf8(); |
| 518 | int nlen = encode_cstring(cname.get_data(), nullptr); |
| 519 | MAKE_ROOM(1 + 1 + 4 + 4 + 4 + 4 * sync_ids.size() + 4 + nlen + (is_custom ? 4 + spawn_arg_size : 0) + state_size); |
| 520 | uint8_t *ptr = packet_cache.ptrw(); |
| 521 | ptr[0] = (uint8_t)SceneMultiplayer::NETWORK_COMMAND_SPAWN; |
| 522 | ptr[1] = scene_id; |
| 523 | int ofs = 2; |
| 524 | ofs += encode_uint32(path_id, &ptr[ofs]); |
| 525 | ofs += encode_uint32(nid, &ptr[ofs]); |
| 526 | ofs += encode_uint32(sync_ids.size(), &ptr[ofs]); |
| 527 | ofs += encode_uint32(nlen, &ptr[ofs]); |
| 528 | for (uint32_t snid : sync_ids) { |
| 529 | ofs += encode_uint32(snid, &ptr[ofs]); |
| 530 | } |
| 531 | ofs += encode_cstring(cname.get_data(), &ptr[ofs]); |
| 532 | // Write args |
| 533 | if (is_custom) { |
| 534 | ofs += encode_uint32(spawn_arg_size, &ptr[ofs]); |
| 535 | Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, &ptr[ofs], spawn_arg_size, false); |
| 536 | ERR_FAIL_COND_V(err, err); |
| 537 | ofs += spawn_arg_size; |
| 538 | } |
| 539 | // Write state. |
| 540 | if (state_size) { |
| 541 | Error err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), &ptr[ofs], state_size); |
| 542 | ERR_FAIL_COND_V(err, err); |
| 543 | ofs += state_size; |
| 544 | } |
| 545 | r_len = ofs; |
| 546 | return OK; |
| 547 | } |
| 548 | |
| 549 | Error SceneReplicationInterface::_make_despawn_packet(Node *p_node, int &r_len) { |
| 550 | const ObjectID oid = p_node->get_instance_id(); |
| 551 | const TrackedNode *tnode = tracked_nodes.getptr(oid); |
| 552 | ERR_FAIL_COND_V(!tnode, ERR_INVALID_PARAMETER); |
| 553 | MAKE_ROOM(5); |
| 554 | uint8_t *ptr = packet_cache.ptrw(); |
| 555 | ptr[0] = (uint8_t)SceneMultiplayer::NETWORK_COMMAND_DESPAWN; |
| 556 | int ofs = 1; |
| 557 | uint32_t nid = tnode->net_id; |
| 558 | ofs += encode_uint32(nid, &ptr[ofs]); |
| 559 | r_len = ofs; |
| 560 | return OK; |
| 561 | } |
| 562 | |
| 563 | Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { |
| 564 | ERR_FAIL_COND_V_MSG(p_buffer_len < 18, ERR_INVALID_DATA, "Invalid spawn packet received" ); |
| 565 | int ofs = 1; // The spawn/despawn command. |
| 566 | uint8_t scene_id = p_buffer[ofs]; |
| 567 | ofs += 1; |
| 568 | uint32_t node_target = decode_uint32(&p_buffer[ofs]); |
| 569 | ofs += 4; |
| 570 | MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(multiplayer->get_path_cache()->get_cached_object(p_from, node_target)); |
| 571 | ERR_FAIL_COND_V(!spawner, ERR_DOES_NOT_EXIST); |
| 572 | ERR_FAIL_COND_V(p_from != spawner->get_multiplayer_authority(), ERR_UNAUTHORIZED); |
| 573 | |
| 574 | uint32_t net_id = decode_uint32(&p_buffer[ofs]); |
| 575 | ofs += 4; |
| 576 | uint32_t sync_len = decode_uint32(&p_buffer[ofs]); |
| 577 | ofs += 4; |
| 578 | uint32_t name_len = decode_uint32(&p_buffer[ofs]); |
| 579 | ofs += 4; |
| 580 | ERR_FAIL_COND_V_MSG(name_len + (sync_len * 4) > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA, vformat("Invalid spawn packet size: %d, wants: %d" , p_buffer_len, ofs + name_len + (sync_len * 4))); |
| 581 | List<uint32_t> sync_ids; |
| 582 | for (uint32_t i = 0; i < sync_len; i++) { |
| 583 | sync_ids.push_back(decode_uint32(&p_buffer[ofs])); |
| 584 | ofs += 4; |
| 585 | } |
| 586 | ERR_FAIL_COND_V_MSG(name_len < 1, ERR_INVALID_DATA, "Zero spawn name size." ); |
| 587 | |
| 588 | // We need to make sure no trickery happens here, but we want to allow autogenerated ("@") node names. |
| 589 | const String name = String::utf8((const char *)&p_buffer[ofs], name_len); |
| 590 | ERR_FAIL_COND_V_MSG(name.validate_node_name() != name, ERR_INVALID_DATA, vformat("Invalid node name received: '%s'. Make sure to add nodes via 'add_child(node, true)' remotely." , name)); |
| 591 | ofs += name_len; |
| 592 | |
| 593 | // Check that we can spawn. |
| 594 | Node *parent = spawner->get_node_or_null(spawner->get_spawn_path()); |
| 595 | ERR_FAIL_COND_V(!parent, ERR_UNCONFIGURED); |
| 596 | ERR_FAIL_COND_V(parent->has_node(name), ERR_INVALID_DATA); |
| 597 | |
| 598 | Node *node = nullptr; |
| 599 | if (scene_id == MultiplayerSpawner::INVALID_ID) { |
| 600 | // Custom spawn. |
| 601 | ERR_FAIL_COND_V(p_buffer_len - ofs < 4, ERR_INVALID_DATA); |
| 602 | uint32_t arg_size = decode_uint32(&p_buffer[ofs]); |
| 603 | ofs += 4; |
| 604 | ERR_FAIL_COND_V(arg_size > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA); |
| 605 | Variant v; |
| 606 | Error err = MultiplayerAPI::decode_and_decompress_variant(v, &p_buffer[ofs], arg_size, nullptr, false); |
| 607 | ERR_FAIL_COND_V(err != OK, err); |
| 608 | ofs += arg_size; |
| 609 | node = spawner->instantiate_custom(v); |
| 610 | } else { |
| 611 | // Scene based spawn. |
| 612 | node = spawner->instantiate_scene(scene_id); |
| 613 | } |
| 614 | ERR_FAIL_COND_V(!node, ERR_UNAUTHORIZED); |
| 615 | node->set_name(name); |
| 616 | |
| 617 | // Add and track remote |
| 618 | ERR_FAIL_COND_V(!peers_info.has(p_from), ERR_UNAVAILABLE); |
| 619 | ERR_FAIL_COND_V(peers_info[p_from].recv_nodes.has(net_id), ERR_ALREADY_IN_USE); |
| 620 | ObjectID oid = node->get_instance_id(); |
| 621 | TrackedNode &tobj = _track(oid); |
| 622 | tobj.spawner = spawner->get_instance_id(); |
| 623 | tobj.net_id = net_id; |
| 624 | tobj.remote_peer = p_from; |
| 625 | peers_info[p_from].recv_nodes[net_id] = oid; |
| 626 | |
| 627 | // The initial state will be applied during the sync config (i.e. before _ready). |
| 628 | pending_spawn = node->get_instance_id(); |
| 629 | pending_spawn_remote = p_from; |
| 630 | pending_buffer_size = p_buffer_len - ofs; |
| 631 | pending_buffer = pending_buffer_size > 0 ? &p_buffer[ofs] : nullptr; |
| 632 | pending_sync_net_ids = sync_ids; |
| 633 | |
| 634 | parent->add_child(node); |
| 635 | spawner->emit_signal(SNAME("spawned" ), node); |
| 636 | |
| 637 | pending_spawn = ObjectID(); |
| 638 | pending_spawn_remote = 0; |
| 639 | pending_buffer = nullptr; |
| 640 | pending_buffer_size = 0; |
| 641 | if (pending_sync_net_ids.size()) { |
| 642 | pending_sync_net_ids.clear(); |
| 643 | ERR_FAIL_V(ERR_INVALID_DATA); // Should have been consumed. |
| 644 | } |
| 645 | return OK; |
| 646 | } |
| 647 | |
| 648 | Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { |
| 649 | ERR_FAIL_COND_V_MSG(p_buffer_len < 5, ERR_INVALID_DATA, "Invalid spawn packet received" ); |
| 650 | int ofs = 1; // The spawn/despawn command. |
| 651 | uint32_t net_id = decode_uint32(&p_buffer[ofs]); |
| 652 | ofs += 4; |
| 653 | |
| 654 | // Untrack remote |
| 655 | ERR_FAIL_COND_V(!peers_info.has(p_from), ERR_UNAUTHORIZED); |
| 656 | PeerInfo &pinfo = peers_info[p_from]; |
| 657 | ERR_FAIL_COND_V(!pinfo.recv_nodes.has(net_id), ERR_UNAUTHORIZED); |
| 658 | Node *node = get_id_as<Node>(pinfo.recv_nodes[net_id]); |
| 659 | ERR_FAIL_COND_V(!node, ERR_BUG); |
| 660 | pinfo.recv_nodes.erase(net_id); |
| 661 | |
| 662 | const ObjectID oid = node->get_instance_id(); |
| 663 | ERR_FAIL_COND_V(!tracked_nodes.has(oid), ERR_BUG); |
| 664 | MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tracked_nodes[oid].spawner); |
| 665 | ERR_FAIL_COND_V(!spawner, ERR_DOES_NOT_EXIST); |
| 666 | ERR_FAIL_COND_V(p_from != spawner->get_multiplayer_authority(), ERR_UNAUTHORIZED); |
| 667 | |
| 668 | if (node->get_parent() != nullptr) { |
| 669 | node->get_parent()->remove_child(node); |
| 670 | } |
| 671 | node->queue_free(); |
| 672 | spawner->emit_signal(SNAME("despawned" ), node); |
| 673 | |
| 674 | return OK; |
| 675 | } |
| 676 | |
| 677 | bool SceneReplicationInterface::_verify_synchronizer(int p_peer, MultiplayerSynchronizer *p_sync, uint32_t &r_net_id) { |
| 678 | r_net_id = p_sync->get_net_id(); |
| 679 | if (r_net_id == 0 || (r_net_id & 0x80000000)) { |
| 680 | int path_id = 0; |
| 681 | bool verified = multiplayer->get_path_cache()->send_object_cache(p_sync, p_peer, path_id); |
| 682 | ERR_FAIL_COND_V_MSG(path_id < 0, false, "This should never happen!" ); |
| 683 | if (r_net_id == 0) { |
| 684 | // First time path based ID. |
| 685 | r_net_id = path_id | 0x80000000; |
| 686 | p_sync->set_net_id(r_net_id | 0x80000000); |
| 687 | } |
| 688 | return verified; |
| 689 | } |
| 690 | return true; |
| 691 | } |
| 692 | |
| 693 | MultiplayerSynchronizer *SceneReplicationInterface::_find_synchronizer(int p_peer, uint32_t p_net_id) { |
| 694 | MultiplayerSynchronizer *sync = nullptr; |
| 695 | if (p_net_id & 0x80000000) { |
| 696 | sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_path_cache()->get_cached_object(p_peer, p_net_id & 0x7FFFFFFF)); |
| 697 | } else if (peers_info[p_peer].recv_sync_ids.has(p_net_id)) { |
| 698 | const ObjectID &sid = peers_info[p_peer].recv_sync_ids[p_net_id]; |
| 699 | sync = get_id_as<MultiplayerSynchronizer>(sid); |
| 700 | } |
| 701 | return sync; |
| 702 | } |
| 703 | |
| 704 | void SceneReplicationInterface::_send_delta(int p_peer, const HashSet<ObjectID> p_synchronizers, uint64_t p_usec, const HashMap<ObjectID, uint64_t> p_last_watch_usecs) { |
| 705 | MAKE_ROOM(/* header */ 1 + /* element */ 4 + 8 + 4 + delta_mtu); |
| 706 | uint8_t *ptr = packet_cache.ptrw(); |
| 707 | ptr[0] = SceneMultiplayer::NETWORK_COMMAND_SYNC | (1 << SceneMultiplayer::CMD_FLAG_0_SHIFT); |
| 708 | int ofs = 1; |
| 709 | for (const ObjectID &oid : p_synchronizers) { |
| 710 | MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(oid); |
| 711 | ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid() || !sync->is_multiplayer_authority()); |
| 712 | uint32_t net_id; |
| 713 | if (!_verify_synchronizer(p_peer, sync, net_id)) { |
| 714 | continue; |
| 715 | } |
| 716 | uint64_t last_usec = p_last_watch_usecs.has(oid) ? p_last_watch_usecs[oid] : 0; |
| 717 | uint64_t indexes; |
| 718 | List<Variant> delta = sync->get_delta_state(p_usec, last_usec, indexes); |
| 719 | |
| 720 | if (!delta.size()) { |
| 721 | continue; // Nothing to update. |
| 722 | } |
| 723 | |
| 724 | Vector<const Variant *> varp; |
| 725 | varp.resize(delta.size()); |
| 726 | const Variant **vptr = varp.ptrw(); |
| 727 | int i = 0; |
| 728 | for (const Variant &v : delta) { |
| 729 | vptr[i] = &v; |
| 730 | i++; |
| 731 | } |
| 732 | int size; |
| 733 | Error err = MultiplayerAPI::encode_and_compress_variants(vptr, varp.size(), nullptr, size); |
| 734 | ERR_CONTINUE_MSG(err != OK, "Unable to encode delta state." ); |
| 735 | |
| 736 | ERR_CONTINUE_MSG(size > delta_mtu, vformat("Synchronizer delta bigger than MTU will not be sent (%d > %d): %s" , size, delta_mtu, sync->get_path())); |
| 737 | |
| 738 | if (ofs + 4 + 8 + 4 + size > delta_mtu) { |
| 739 | // Send what we got, and reset write. |
| 740 | _send_raw(packet_cache.ptr(), ofs, p_peer, true); |
| 741 | ofs = 1; |
| 742 | } |
| 743 | if (size) { |
| 744 | ofs += encode_uint32(sync->get_net_id(), &ptr[ofs]); |
| 745 | ofs += encode_uint64(indexes, &ptr[ofs]); |
| 746 | ofs += encode_uint32(size, &ptr[ofs]); |
| 747 | MultiplayerAPI::encode_and_compress_variants(vptr, varp.size(), &ptr[ofs], size); |
| 748 | ofs += size; |
| 749 | } |
| 750 | #ifdef DEBUG_ENABLED |
| 751 | _profile_node_data("delta_out" , oid, size); |
| 752 | #endif |
| 753 | peers_info[p_peer].last_watch_usecs[oid] = p_usec; |
| 754 | } |
| 755 | if (ofs > 1) { |
| 756 | // Got some left over to send. |
| 757 | _send_raw(packet_cache.ptr(), ofs, p_peer, true); |
| 758 | } |
| 759 | } |
| 760 | |
| 761 | Error SceneReplicationInterface::on_delta_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { |
| 762 | int ofs = 1; |
| 763 | while (ofs + 4 + 8 + 4 < p_buffer_len) { |
| 764 | uint32_t net_id = decode_uint32(&p_buffer[ofs]); |
| 765 | ofs += 4; |
| 766 | uint64_t indexes = decode_uint64(&p_buffer[ofs]); |
| 767 | ofs += 8; |
| 768 | uint32_t size = decode_uint32(&p_buffer[ofs]); |
| 769 | ofs += 4; |
| 770 | ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA); |
| 771 | MultiplayerSynchronizer *sync = _find_synchronizer(p_from, net_id); |
| 772 | Node *node = sync ? sync->get_root_node() : nullptr; |
| 773 | if (!sync || sync->get_multiplayer_authority() != p_from || !node) { |
| 774 | ofs += size; |
| 775 | ERR_CONTINUE_MSG(true, "Ignoring delta for non-authority or invalid synchronizer." ); |
| 776 | } |
| 777 | List<NodePath> props = sync->get_delta_properties(indexes); |
| 778 | ERR_FAIL_COND_V(props.size() == 0, ERR_INVALID_DATA); |
| 779 | Vector<Variant> vars; |
| 780 | vars.resize(props.size()); |
| 781 | int consumed = 0; |
| 782 | Error err = MultiplayerAPI::decode_and_decompress_variants(vars, p_buffer + ofs, size, consumed); |
| 783 | ERR_FAIL_COND_V(err != OK, err); |
| 784 | ERR_FAIL_COND_V(uint32_t(consumed) != size, ERR_INVALID_DATA); |
| 785 | err = MultiplayerSynchronizer::set_state(props, node, vars); |
| 786 | ERR_FAIL_COND_V(err != OK, err); |
| 787 | ofs += size; |
| 788 | sync->emit_signal(SNAME("delta_synchronized" )); |
| 789 | #ifdef DEBUG_ENABLED |
| 790 | _profile_node_data("delta_in" , sync->get_instance_id(), size); |
| 791 | #endif |
| 792 | } |
| 793 | return OK; |
| 794 | } |
| 795 | |
| 796 | void SceneReplicationInterface::_send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_usec) { |
| 797 | MAKE_ROOM(/* header */ 3 + /* element */ 4 + 4 + sync_mtu); |
| 798 | uint8_t *ptr = packet_cache.ptrw(); |
| 799 | ptr[0] = SceneMultiplayer::NETWORK_COMMAND_SYNC; |
| 800 | int ofs = 1; |
| 801 | ofs += encode_uint16(p_sync_net_time, &ptr[1]); |
| 802 | // Can only send updates for already notified nodes. |
| 803 | // This is a lazy implementation, we could optimize much more here with by grouping by replication config. |
| 804 | for (const ObjectID &oid : p_synchronizers) { |
| 805 | MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(oid); |
| 806 | ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid() || !sync->is_multiplayer_authority()); |
| 807 | if (!sync->update_outbound_sync_time(p_usec)) { |
| 808 | continue; // nothing to sync. |
| 809 | } |
| 810 | |
| 811 | Node *node = sync->get_root_node(); |
| 812 | ERR_CONTINUE(!node); |
| 813 | uint32_t net_id = sync->get_net_id(); |
| 814 | if (!_verify_synchronizer(p_peer, sync, net_id)) { |
| 815 | // The path based sync is not yet confirmed, skipping. |
| 816 | continue; |
| 817 | } |
| 818 | int size; |
| 819 | Vector<Variant> vars; |
| 820 | Vector<const Variant *> varp; |
| 821 | const List<NodePath> props = sync->get_replication_config()->get_sync_properties(); |
| 822 | Error err = MultiplayerSynchronizer::get_state(props, node, vars, varp); |
| 823 | ERR_CONTINUE_MSG(err != OK, "Unable to retrieve sync state." ); |
| 824 | err = MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), nullptr, size); |
| 825 | ERR_CONTINUE_MSG(err != OK, "Unable to encode sync state." ); |
| 826 | // TODO Handle single state above MTU. |
| 827 | ERR_CONTINUE_MSG(size > sync_mtu, vformat("Node states bigger than MTU will not be sent (%d > %d): %s" , size, sync_mtu, node->get_path())); |
| 828 | if (ofs + 4 + 4 + size > sync_mtu) { |
| 829 | // Send what we got, and reset write. |
| 830 | _send_raw(packet_cache.ptr(), ofs, p_peer, false); |
| 831 | ofs = 3; |
| 832 | } |
| 833 | if (size) { |
| 834 | ofs += encode_uint32(sync->get_net_id(), &ptr[ofs]); |
| 835 | ofs += encode_uint32(size, &ptr[ofs]); |
| 836 | MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), &ptr[ofs], size); |
| 837 | ofs += size; |
| 838 | } |
| 839 | #ifdef DEBUG_ENABLED |
| 840 | _profile_node_data("sync_out" , oid, size); |
| 841 | #endif |
| 842 | } |
| 843 | if (ofs > 3) { |
| 844 | // Got some left over to send. |
| 845 | _send_raw(packet_cache.ptr(), ofs, p_peer, false); |
| 846 | } |
| 847 | } |
| 848 | |
| 849 | Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { |
| 850 | ERR_FAIL_COND_V_MSG(p_buffer_len < 11, ERR_INVALID_DATA, "Invalid sync packet received" ); |
| 851 | bool is_delta = (p_buffer[0] & (1 << SceneMultiplayer::CMD_FLAG_0_SHIFT)) != 0; |
| 852 | if (is_delta) { |
| 853 | return on_delta_receive(p_from, p_buffer, p_buffer_len); |
| 854 | } |
| 855 | uint16_t time = decode_uint16(&p_buffer[1]); |
| 856 | int ofs = 3; |
| 857 | while (ofs + 8 < p_buffer_len) { |
| 858 | uint32_t net_id = decode_uint32(&p_buffer[ofs]); |
| 859 | ofs += 4; |
| 860 | uint32_t size = decode_uint32(&p_buffer[ofs]); |
| 861 | ofs += 4; |
| 862 | ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA); |
| 863 | MultiplayerSynchronizer *sync = _find_synchronizer(p_from, net_id); |
| 864 | if (!sync) { |
| 865 | // Not received yet. |
| 866 | ofs += size; |
| 867 | continue; |
| 868 | } |
| 869 | Node *node = sync->get_root_node(); |
| 870 | if (sync->get_multiplayer_authority() != p_from || !node) { |
| 871 | // Not valid for me. |
| 872 | ofs += size; |
| 873 | ERR_CONTINUE_MSG(true, "Ignoring sync data from non-authority or for missing node." ); |
| 874 | } |
| 875 | if (!sync->update_inbound_sync_time(time)) { |
| 876 | // State is too old. |
| 877 | ofs += size; |
| 878 | continue; |
| 879 | } |
| 880 | const List<NodePath> props = sync->get_replication_config()->get_sync_properties(); |
| 881 | Vector<Variant> vars; |
| 882 | vars.resize(props.size()); |
| 883 | int consumed; |
| 884 | Error err = MultiplayerAPI::decode_and_decompress_variants(vars, &p_buffer[ofs], size, consumed); |
| 885 | ERR_FAIL_COND_V(err, err); |
| 886 | err = MultiplayerSynchronizer::set_state(props, node, vars); |
| 887 | ERR_FAIL_COND_V(err, err); |
| 888 | ofs += size; |
| 889 | sync->emit_signal(SNAME("synchronized" )); |
| 890 | #ifdef DEBUG_ENABLED |
| 891 | _profile_node_data("sync_in" , sync->get_instance_id(), size); |
| 892 | #endif |
| 893 | } |
| 894 | return OK; |
| 895 | } |
| 896 | |
| 897 | void SceneReplicationInterface::set_max_sync_packet_size(int p_size) { |
| 898 | ERR_FAIL_COND_MSG(p_size < 128, "Sync maximum packet size must be at least 128 bytes." ); |
| 899 | sync_mtu = p_size; |
| 900 | } |
| 901 | |
| 902 | int SceneReplicationInterface::get_max_sync_packet_size() const { |
| 903 | return sync_mtu; |
| 904 | } |
| 905 | |
| 906 | void SceneReplicationInterface::set_max_delta_packet_size(int p_size) { |
| 907 | ERR_FAIL_COND_MSG(p_size < 128, "Sync maximum packet size must be at least 128 bytes." ); |
| 908 | delta_mtu = p_size; |
| 909 | } |
| 910 | |
| 911 | int SceneReplicationInterface::get_max_delta_packet_size() const { |
| 912 | return delta_mtu; |
| 913 | } |
| 914 | |