Merge pull request #68678 from Faless/mp/4.x_rpc_visibility

[MP] RPC visibility.
This commit is contained in:
Rémi Verschelde 2022-11-20 15:37:17 +01:00
commit f9c042102b
No known key found for this signature in database
GPG Key ID: C3336907360768E1
4 changed files with 79 additions and 21 deletions

View File

@ -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();

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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]));