diff options
-rw-r--r-- | modules/multiplayer/scene_multiplayer.h | 1 | ||||
-rw-r--r-- | modules/multiplayer/scene_replication_interface.cpp | 43 | ||||
-rw-r--r-- | modules/multiplayer/scene_replication_interface.h | 2 | ||||
-rw-r--r-- | modules/multiplayer/scene_rpc_interface.cpp | 54 |
4 files changed, 79 insertions, 21 deletions
diff --git a/modules/multiplayer/scene_multiplayer.h b/modules/multiplayer/scene_multiplayer.h index da8c762134..4482495899 100644 --- a/modules/multiplayer/scene_multiplayer.h +++ b/modules/multiplayer/scene_multiplayer.h @@ -171,6 +171,7 @@ public: bool is_server_relay_enabled() const; Ref<SceneCacheInterface> get_path_cache() { return cache; } + Ref<SceneReplicationInterface> get_replicator() { return replicator; } SceneMultiplayer(); ~SceneMultiplayer(); diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp index 9302ea4f20..7d9437936a 100644 --- a/modules/multiplayer/scene_replication_interface.cpp +++ b/modules/multiplayer/scene_replication_interface.cpp @@ -257,15 +257,54 @@ void SceneReplicationInterface::_visibility_changed(int p_peer, ObjectID p_sid) Node *node = sync->get_root_node(); ERR_FAIL_COND(!node); // Bug. const ObjectID oid = node->get_instance_id(); - if (spawned_nodes.has(oid)) { + if (spawned_nodes.has(oid) && p_peer != multiplayer->get_unique_id()) { _update_spawn_visibility(p_peer, oid); } _update_sync_visibility(p_peer, sync); } +bool SceneReplicationInterface::is_rpc_visible(const ObjectID &p_oid, int p_peer) const { + if (!tracked_nodes.has(p_oid)) { + return true; // Untracked nodes are always visible to RPCs. + } + ERR_FAIL_COND_V(p_peer < 0, false); + const TrackedNode &tnode = tracked_nodes[p_oid]; + if (tnode.synchronizers.is_empty()) { + return true; // No synchronizers means no visibility restrictions. + } + if (tnode.remote_peer && uint32_t(p_peer) == tnode.remote_peer) { + return true; // RPCs on spawned nodes are always visible to spawner. + } else if (spawned_nodes.has(p_oid)) { + // It's a spwaned node we control, this can be fast + if (p_peer) { + return peers_info.has(p_peer) && peers_info[p_peer].spawn_nodes.has(p_oid); + } else { + for (const KeyValue<int, PeerInfo> &E : peers_info) { + if (!E.value.spawn_nodes.has(p_oid)) { + return false; // Not public. + } + } + return true; // All peers have this node. + } + } else { + // Cycle object synchronizers to check visibility. + for (const ObjectID &sid : tnode.synchronizers) { + MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(sid); + ERR_CONTINUE(!sync); + // RPC visibility is composed using OR when multiple synchronizers are present. + // Note that we don't really care about authority here which may lead to unexpected + // results when using multiple synchronizers to control the same node. + if (sync->is_visible_to(p_peer)) { + return true; + } + } + return false; // Not visible. + } +} + Error SceneReplicationInterface::_update_sync_visibility(int p_peer, MultiplayerSynchronizer *p_sync) { ERR_FAIL_COND_V(!p_sync, ERR_BUG); - if (!multiplayer->has_multiplayer_peer() || !p_sync->is_multiplayer_authority()) { + if (!multiplayer->has_multiplayer_peer() || !p_sync->is_multiplayer_authority() || p_peer == multiplayer->get_unique_id()) { return OK; } diff --git a/modules/multiplayer/scene_replication_interface.h b/modules/multiplayer/scene_replication_interface.h index ad68425d77..30d58f7129 100644 --- a/modules/multiplayer/scene_replication_interface.h +++ b/modules/multiplayer/scene_replication_interface.h @@ -125,6 +125,8 @@ public: Error on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len); Error on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len); + bool is_rpc_visible(const ObjectID &p_oid, int p_peer) const; + SceneReplicationInterface(SceneMultiplayer *p_multiplayer) { multiplayer = p_multiplayer; } diff --git a/modules/multiplayer/scene_rpc_interface.cpp b/modules/multiplayer/scene_rpc_interface.cpp index bfe54c00ff..dbf2b3751e 100644 --- a/modules/multiplayer/scene_rpc_interface.cpp +++ b/modules/multiplayer/scene_rpc_interface.cpp @@ -295,7 +295,7 @@ void SceneRPCInterface::_process_rpc(Node *p_node, const uint16_t p_rpc_method_i } } -void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) { +void SceneRPCInterface::_send_rpc(Node *p_node, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) { Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); ERR_FAIL_COND_MSG(peer.is_null(), "Attempt to call RPC without active multiplayer peer."); @@ -311,12 +311,35 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + "."); } - // See if all peers have cached path (if so, call can be fast). - int psc_id; - const bool has_all_peers = multiplayer->get_path_cache()->send_object_cache(p_from, p_to, psc_id); + // See if all peers have cached path (if so, call can be fast) while building the RPC target list. + HashSet<int> targets; + Ref<SceneCacheInterface> cache = multiplayer->get_path_cache(); + int psc_id = -1; + bool has_all_peers = true; + const ObjectID oid = p_node->get_instance_id(); + if (p_to > 0) { + ERR_FAIL_COND_MSG(!multiplayer->get_replicator()->is_rpc_visible(oid, p_to), "Attempt to call an RPC to a peer that cannot see this node. Peer ID: " + itos(p_to)); + targets.insert(p_to); + has_all_peers = cache->send_object_cache(p_node, p_to, psc_id); + } else { + bool restricted = !multiplayer->get_replicator()->is_rpc_visible(oid, 0); + for (const int &P : multiplayer->get_connected_peers()) { + if (p_to < 0 && P == -p_to) { + continue; // Excluded peer. + } + if (restricted && !multiplayer->get_replicator()->is_rpc_visible(oid, P)) { + continue; // Not visible to this peer. + } + targets.insert(P); + bool has_peer = cache->send_object_cache(p_node, P, psc_id); + has_all_peers = has_all_peers && has_peer; + } + } + if (targets.is_empty()) { + return; // No one in sight. + } // Create base packet, lots of hardcode because it must be tight. - int ofs = 0; #define MAKE_ROOM(m_amount) \ @@ -399,7 +422,7 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con ERR_FAIL_COND(name_id_compression > 1); #ifdef DEBUG_ENABLED - _profile_node_data("rpc_out", p_from->get_instance_id(), ofs); + _profile_node_data("rpc_out", p_node->get_instance_id(), ofs); #endif // We can now set the meta @@ -410,8 +433,9 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con peer->set_transfer_mode(p_config.transfer_mode); if (has_all_peers) { - // They all have verified paths, so send fast. - multiplayer->send_command(p_to, packet_cache.ptr(), ofs); + for (const int P : targets) { + multiplayer->send_command(P, packet_cache.ptr(), ofs); + } } else { // Unreachable because the node ID is never compressed if the peers doesn't know it. CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32); @@ -419,23 +443,15 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con // Not all verified path, so send one by one. // Append path at the end, since we will need it for some packets. - NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path()); + NodePath from_path = multiplayer->get_root_path().rel_path_to(p_node->get_path()); CharString pname = String(from_path).utf8(); int path_len = encode_cstring(pname.get_data(), nullptr); MAKE_ROOM(ofs + path_len); encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); - for (const int &P : multiplayer->get_connected_peers()) { - if (p_to < 0 && P == -p_to) { - continue; // Continue, excluded. - } - - if (p_to > 0 && P != p_to) { - continue; // Continue, not for this peer. - } - + // Not all verified path, so check which needs the longer packet. + for (const int P : targets) { bool confirmed = multiplayer->get_path_cache()->is_cache_confirmed(from_path, P); - if (confirmed) { // This one confirmed path, so use id. encode_uint32(psc_id, &(packet_cache.write[1])); |