Merge pull request #68678 from Faless/mp/4.x_rpc_visibility
[MP] RPC visibility.
This commit is contained in:
commit
f9c042102b
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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]));
|
||||
|
|
Loading…
Reference in New Issue