summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/multiplayer/scene_multiplayer.h1
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp43
-rw-r--r--modules/multiplayer/scene_replication_interface.h2
-rw-r--r--modules/multiplayer/scene_rpc_interface.cpp54
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]));