diff options
Diffstat (limited to 'core/io/multiplayer_api.cpp')
-rw-r--r-- | core/io/multiplayer_api.cpp | 506 |
1 files changed, 203 insertions, 303 deletions
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 2f17d9e746..0ce9a70921 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,48 +32,63 @@ #include "core/debugger/engine_debugger.h" #include "core/io/marshalls.h" +#include "core/io/multiplayer_replicator.h" #include "scene/main/node.h" #include <stdint.h> -#define NODE_ID_COMPRESSION_SHIFT 3 -#define NAME_ID_COMPRESSION_SHIFT 5 -#define BYTE_ONLY_OR_NO_ARGS_SHIFT 6 - #ifdef DEBUG_ENABLED #include "core/os/os.h" #endif -_FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) { - switch (mode) { - case MultiplayerAPI::RPC_MODE_DISABLED: { - // Do nothing. - } break; - case MultiplayerAPI::RPC_MODE_REMOTE: { - // Do nothing also. Remote cannot produce a local call. - } break; - case MultiplayerAPI::RPC_MODE_MASTERSYNC: { - if (is_master) { - r_skip_rpc = true; // I am the master, so skip remote call. - } - [[fallthrough]]; +String _get_rpc_md5(const Node *p_node) { + String rpc_list; + const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods(); + for (int i = 0; i < node_config.size(); i++) { + rpc_list += String(node_config[i].name); + } + if (p_node->get_script_instance()) { + const Vector<MultiplayerAPI::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods(); + for (int i = 0; i < script_config.size(); i++) { + rpc_list += String(script_config[i].name); } - case MultiplayerAPI::RPC_MODE_REMOTESYNC: - case MultiplayerAPI::RPC_MODE_PUPPETSYNC: { - // Call it, sync always results in a local call. - return true; - } break; - case MultiplayerAPI::RPC_MODE_MASTER: { - if (is_master) { - r_skip_rpc = true; // I am the master, so skip remote call. + } + return rpc_list.md5_text(); +} + +const MultiplayerAPI::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) { + const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods(); + for (int i = 0; i < node_config.size(); i++) { + if (node_config[i].name == p_method) { + r_id = ((uint16_t)i) | (1 << 15); + return node_config[i]; + } + } + if (p_node->get_script_instance()) { + const Vector<MultiplayerAPI::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods(); + for (int i = 0; i < script_config.size(); i++) { + if (script_config[i].name == p_method) { + r_id = (uint16_t)i; + return script_config[i]; } - return is_master; - } break; - case MultiplayerAPI::RPC_MODE_PUPPET: { - return !is_master; - } break; + } } - return false; + return MultiplayerAPI::RPCConfig(); +} + +const MultiplayerAPI::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) { + Vector<MultiplayerAPI::RPCConfig> config; + uint16_t id = p_id; + if (id & (1 << 15)) { + id = id & ~(1 << 15); + config = p_node->get_node_rpc_methods(); + } else if (p_node->get_script_instance()) { + config = p_node->get_script_instance()->get_rpc_methods(); + } + if (id < config.size()) { + return config[id]; + } + return MultiplayerAPI::RPCConfig(); } _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) { @@ -81,15 +96,12 @@ _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, i case MultiplayerAPI::RPC_MODE_DISABLED: { return false; } break; - case MultiplayerAPI::RPC_MODE_REMOTE: - case MultiplayerAPI::RPC_MODE_REMOTESYNC: { + case MultiplayerAPI::RPC_MODE_REMOTE: { return true; } break; - case MultiplayerAPI::RPC_MODE_MASTERSYNC: case MultiplayerAPI::RPC_MODE_MASTER: { return p_node->is_network_master(); } break; - case MultiplayerAPI::RPC_MODE_PUPPETSYNC: case MultiplayerAPI::RPC_MODE_PUPPET: { return !p_node->is_network_master() && p_remote_id == p_node->get_network_master(); } break; @@ -99,7 +111,7 @@ _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, i } void MultiplayerAPI::poll() { - if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) { + if (!network_peer.is_valid() || network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) { return; } @@ -131,6 +143,7 @@ void MultiplayerAPI::poll() { } void MultiplayerAPI::clear() { + replicator->clear(); connected_peers.clear(); path_get_cache.clear(); path_send_cache.clear(); @@ -142,13 +155,17 @@ void MultiplayerAPI::set_root_node(Node *p_node) { root_node = p_node; } -void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer) { +Node *MultiplayerAPI::get_root_node() { + return root_node; +} + +void MultiplayerAPI::set_network_peer(const Ref<MultiplayerPeer> &p_peer) { if (p_peer == network_peer) { return; // Nothing to do } - ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, - "Supplied NetworkedMultiplayerPeer must be connecting or connected."); + ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, + "Supplied MultiplayerPeer must be connecting or connected."); if (network_peer.is_valid()) { network_peer->disconnect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); @@ -170,7 +187,7 @@ void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_pee } } -Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const { +Ref<MultiplayerPeer> MultiplayerAPI::get_network_peer() const { return network_peer; } @@ -227,8 +244,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ _process_confirm_path(p_from, p_packet, p_packet_len); } break; - case NETWORK_COMMAND_REMOTE_CALL: - case NETWORK_COMMAND_REMOTE_SET: { + case NETWORK_COMMAND_REMOTE_CALL: { // Extract packet meta int packet_min_size = 1; int name_id_offset = 1; @@ -298,18 +314,18 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ } const int packet_len = get_packet_len(node_target, p_packet_len); - if (packet_type == NETWORK_COMMAND_REMOTE_CALL) { - _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size); - - } else { - _process_rset(node, name_id, p_from, p_packet, packet_len, packet_min_size); - } - + _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size); } break; case NETWORK_COMMAND_RAW: { _process_raw(p_from, p_packet, p_packet_len); } break; + case NETWORK_COMMAND_SPAWN: { + replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, true); + } break; + case NETWORK_COMMAND_DESPAWN: { + replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, false); + } break; } } @@ -318,7 +334,6 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uin if (p_node_target & 0x80000000) { // Use full path (not cached yet). - int ofs = p_node_target & 0x7FFFFFFF; ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared."); @@ -333,41 +348,22 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uin if (!node) { ERR_PRINT("Failed to get path from RPC: " + String(np) + "."); } + return node; } else { // Use cached path. - int id = p_node_target; - - Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); - ERR_FAIL_COND_V_MSG(!E, nullptr, "Invalid packet received. Requests invalid peer cache."); - - Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id); - ERR_FAIL_COND_V_MSG(!F, nullptr, "Invalid packet received. Unabled to find requested cached node."); - - PathGetCache::NodeInfo *ni = &F->get(); - // Do proper caching later. - - node = root_node->get_node(ni->path); - if (!node) { - ERR_PRINT("Failed to get cached path from RPC: " + String(ni->path) + "."); - } + return get_cached_node(p_from, p_node_target); } - return node; } void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small."); // Check that remote can call the RPC on this node. - StringName name = p_node->get_node_rpc_method(p_rpc_method_id); - RPCMode rpc_mode = p_node->get_node_rpc_mode_by_id(p_rpc_method_id); - if (name == StringName() && p_node->get_script_instance()) { - name = p_node->get_script_instance()->get_rpc_method(p_rpc_method_id); - rpc_mode = p_node->get_script_instance()->get_rpc_mode_by_id(p_rpc_method_id); - } - ERR_FAIL_COND(name == StringName()); + const RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id); + ERR_FAIL_COND(config.name == StringName()); - bool can_call = _can_call_mode(p_node, rpc_mode, p_from); - ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); + bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from); + ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); int argc = 0; bool byte_only = false; @@ -410,7 +406,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); int vlen; - Error err = _decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); + Error err = decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument."); argp.write[i] = &args[i]; @@ -420,47 +416,14 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, Callable::CallError ce; - p_node->call(name, (const Variant **)argp.ptr(), argc, ce); + p_node->call(config.name, (const Variant **)argp.ptr(), argc, ce); if (ce.error != Callable::CallError::CALL_OK) { - String error = Variant::get_call_error_text(p_node, name, (const Variant **)argp.ptr(), argc, ce); + String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce); error = "RPC - " + error; ERR_PRINT(error); } } -void MultiplayerAPI::_process_rset(Node *p_node, const uint16_t p_rpc_property_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { - ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); - - // Check that remote can call the RSET on this node. - StringName name = p_node->get_node_rset_property(p_rpc_property_id); - RPCMode rset_mode = p_node->get_node_rset_mode_by_id(p_rpc_property_id); - if (name == StringName() && p_node->get_script_instance()) { - name = p_node->get_script_instance()->get_rset_property(p_rpc_property_id); - rset_mode = p_node->get_script_instance()->get_rset_mode_by_id(p_rpc_property_id); - } - ERR_FAIL_COND(name == StringName()); - - bool can_call = _can_call_mode(p_node, rset_mode, p_from); - ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); - -#ifdef DEBUG_ENABLED - _profile_node_data("in_rset", p_node->get_instance_id()); -#endif - - Variant value; - Error err = _decode_and_decompress_variant(value, &p_packet[p_offset], p_packet_len - p_offset, nullptr); - - ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RSET value."); - - bool valid; - - p_node->set(name, value, &valid); - if (!valid) { - String error = "Error setting remote property '" + String(name) + "', not found in object of type " + p_node->get_class() + "."; - ERR_PRINT(error); - } -} - void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small."); int ofs = 1; @@ -483,7 +446,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, Node *node = root_node->get_node(path); ERR_FAIL_COND(node == nullptr); - const bool valid_rpc_checksum = node->get_rpc_md5() == methods_md5; + const bool valid_rpc_checksum = _get_rpc_md5(node) == methods_md5; if (valid_rpc_checksum == false) { ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); } @@ -504,7 +467,8 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, packet.write[1] = valid_rpc_checksum; encode_cstring(pname.get_data(), &packet.write[2]); - network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); + network_peer->set_transfer_channel(0); + network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->set_target_peer(p_from); network_peer->put_packet(packet.ptr(), packet.size()); } @@ -565,7 +529,7 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC const int path_len = encode_cstring(path.get_data(), nullptr); // Extract MD5 from rpc methods list. - const String methods_md5 = p_node->get_rpc_md5(); + const String methods_md5 = _get_rpc_md5(p_node); const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder. Vector<uint8_t> packet; @@ -581,12 +545,13 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC ofs += encode_cstring(path.get_data(), &packet.write[ofs]); - for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) { - network_peer->set_target_peer(E->get()); // To all of you. - network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); + for (int &E : peers_to_add) { + network_peer->set_target_peer(E); // To all of you. + network_peer->set_transfer_channel(0); + network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->put_packet(packet.ptr(), packet.size()); - psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed. + psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed. } } @@ -605,7 +570,7 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC #define ENCODE_16 1 << 5 #define ENCODE_32 2 << 5 #define ENCODE_64 3 << 5 -Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) { +Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) { // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31 CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK); @@ -671,7 +636,7 @@ Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uin return err; } if (r_buffer) { - // The first byte is not used by the marshaling, so store the type + // The first byte is not used by the marshalling, so store the type // so we know how to decompress and decode this variant. r_buffer[0] = p_variant.get_type(); } @@ -680,7 +645,7 @@ Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uin return OK; } -Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) { +Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) { const uint8_t *buf = p_buffer; int len = p_len; @@ -748,12 +713,12 @@ Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const u return OK; } -void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) { +void MultiplayerAPI::_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) { ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree."); - ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree."); + ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree."); - ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected."); + ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected."); ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments >255."); @@ -787,13 +752,13 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p packet_cache.resize(m_amount); // Encode meta. - // The meta is composed by a single byte that contains (starting from the least segnificant bit): + // The meta is composed by a single byte that contains (starting from the least significant bit): // - `NetworkCommands` in the first three bits. // - `NetworkNodeIdCompression` in the next 2 bits. // - `NetworkNameIdCompression` in the next 1 bit. // - `byte_only_or_no_args` in the next 1 bit. // - So we still have the last bit free! - uint8_t command_type = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; + uint8_t command_type = NETWORK_COMMAND_REMOTE_CALL; uint8_t node_id_compression = UINT8_MAX; uint8_t name_id_compression = UINT8_MAX; bool byte_only_or_no_args = false; @@ -826,88 +791,49 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p ofs += 4; } } else { - // The targets doesn't know the node yet, so we need to use 32 bits int. + // The targets don't know the node yet, so we need to use 32 bits int. node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; MAKE_ROOM(ofs + 4); encode_uint32(psc->id, &(packet_cache.write[ofs])); ofs += 4; } - if (p_set) { - // Take the rpc property ID - uint16_t property_id = p_from->get_node_rset_property_id(p_name); - if (property_id == UINT16_MAX && p_from->get_script_instance()) { - property_id = p_from->get_script_instance()->get_rset_property_id(p_name); - } - ERR_FAIL_COND_MSG(property_id == UINT16_MAX, "Unable to take the `property_id` for the property:" + p_name + ". this can happen only if this property is not marked as `remote`."); - - if (property_id <= UINT8_MAX) { - // The ID fits in 1 byte - name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; - MAKE_ROOM(ofs + 1); - packet_cache.write[ofs] = static_cast<uint8_t>(property_id); - ofs += 1; - } else { - // The ID is larger, let's use 2 bytes - name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; - MAKE_ROOM(ofs + 2); - encode_uint16(property_id, &(packet_cache.write[ofs])); - ofs += 2; - } - - // Set argument. - int len(0); - Error err = _encode_and_compress_variant(*p_arg[0], nullptr, len); - ERR_FAIL_COND_MSG(err != OK, "Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!"); - MAKE_ROOM(ofs + len); - _encode_and_compress_variant(*p_arg[0], &(packet_cache.write[ofs]), len); - ofs += len; - + // Encode method ID + if (p_rpc_id <= UINT8_MAX) { + // The ID fits in 1 byte + name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; + MAKE_ROOM(ofs + 1); + packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id); + ofs += 1; } else { - // Take the rpc method ID - uint16_t method_id = p_from->get_node_rpc_method_id(p_name); - if (method_id == UINT16_MAX && p_from->get_script_instance()) { - method_id = p_from->get_script_instance()->get_rpc_method_id(p_name); - } - ERR_FAIL_COND_MSG(method_id == UINT16_MAX, - vformat("Unable to take the `method_id` for the function \"%s\" at path: \"%s\". This happens when the method is not marked as `remote`.", p_name, p_from->get_path())); - - if (method_id <= UINT8_MAX) { - // The ID fits in 1 byte - name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; - MAKE_ROOM(ofs + 1); - packet_cache.write[ofs] = static_cast<uint8_t>(method_id); - ofs += 1; - } else { - // The ID is larger, let's use 2 bytes - name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; - MAKE_ROOM(ofs + 2); - encode_uint16(method_id, &(packet_cache.write[ofs])); - ofs += 2; - } + // The ID is larger, let's use 2 bytes + name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; + MAKE_ROOM(ofs + 2); + encode_uint16(p_rpc_id, &(packet_cache.write[ofs])); + ofs += 2; + } - if (p_argcount == 0) { - byte_only_or_no_args = true; - } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) { - byte_only_or_no_args = true; - // Special optimization when only the byte vector is sent. - const Vector<uint8_t> data = *p_arg[0]; - MAKE_ROOM(ofs + data.size()); - copymem(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size()); - ofs += data.size(); - } else { - // Arguments - MAKE_ROOM(ofs + 1); - packet_cache.write[ofs] = p_argcount; - ofs += 1; - for (int i = 0; i < p_argcount; i++) { - int len(0); - Error err = _encode_and_compress_variant(*p_arg[i], nullptr, len); - ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); - MAKE_ROOM(ofs + len); - _encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len); - ofs += len; - } + if (p_argcount == 0) { + byte_only_or_no_args = true; + } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) { + byte_only_or_no_args = true; + // Special optimization when only the byte vector is sent. + const Vector<uint8_t> data = *p_arg[0]; + MAKE_ROOM(ofs + data.size()); + memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size()); + ofs += data.size(); + } else { + // Arguments + MAKE_ROOM(ofs + 1); + packet_cache.write[ofs] = p_argcount; + ofs += 1; + for (int i = 0; i < p_argcount; i++) { + int len(0); + Error err = encode_and_compress_variant(*p_arg[i], nullptr, len); + ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); + MAKE_ROOM(ofs + len); + encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len); + ofs += len; } } @@ -923,7 +849,8 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p #endif // Take chance and set transfer mode, since all send methods will use it. - network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); + network_peer->set_transfer_channel(p_config.channel); + network_peer->set_transfer_mode(p_config.transfer_mode); if (has_all_peers) { // They all have verified paths, so send fast. @@ -971,7 +898,10 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p void MultiplayerAPI::_add_peer(int p_id) { connected_peers.insert(p_id); path_get_cache.insert(p_id, PathGetCache()); - emit_signal("network_peer_connected", p_id); + if (is_network_server()) { + replicator->spawn_all(p_id); + } + emit_signal(SNAME("network_peer_connected"), p_id); } void MultiplayerAPI::_del_peer(int p_id) { @@ -982,57 +912,51 @@ void MultiplayerAPI::_del_peer(int p_id) { // Some refactoring is needed to make this faster and do paths GC. List<NodePath> keys; path_send_cache.get_key_list(&keys); - for (List<NodePath>::Element *E = keys.front(); E; E = E->next()) { - PathSentCache *psc = path_send_cache.getptr(E->get()); + for (const NodePath &E : keys) { + PathSentCache *psc = path_send_cache.getptr(E); psc->confirmed_peers.erase(p_id); } - emit_signal("network_peer_disconnected", p_id); + emit_signal(SNAME("network_peer_disconnected"), p_id); } void MultiplayerAPI::_connected_to_server() { - emit_signal("connected_to_server"); + emit_signal(SNAME("connected_to_server")); } void MultiplayerAPI::_connection_failed() { - emit_signal("connection_failed"); + emit_signal(SNAME("connection_failed")); } void MultiplayerAPI::_server_disconnected() { - emit_signal("server_disconnected"); + emit_signal(SNAME("server_disconnected")); } void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active."); ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); - ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected."); + ERR_FAIL_COND_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected."); int node_id = network_peer->get_unique_id(); - bool skip_rpc = node_id == p_peer_id; bool call_local_native = false; bool call_local_script = false; - bool is_master = p_node->is_network_master(); - + uint16_t rpc_id = UINT16_MAX; + const RPCConfig config = _get_rpc_config(p_node, p_method, rpc_id); + ERR_FAIL_COND_MSG(config.name == StringName(), + vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, p_node->get_path())); if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { - // Check that send mode can use local call. - - RPCMode rpc_mode = p_node->get_node_rpc_mode(p_method); - call_local_native = _should_call_local(rpc_mode, is_master, skip_rpc); - - if (call_local_native) { - // Done below. - } else if (p_node->get_script_instance()) { - // Attempt with script. - rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method); - call_local_script = _should_call_local(rpc_mode, is_master, skip_rpc); + if (rpc_id & (1 << 15)) { + call_local_native = config.sync; + } else { + call_local_script = config.sync; } } - if (!skip_rpc) { + if (p_peer_id != node_id) { #ifdef DEBUG_ENABLED _profile_node_data("out_rpc", p_node->get_instance_id()); #endif - _send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount); + _send_rpc(p_node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount); } if (call_local_native) { @@ -1064,77 +988,13 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const } } - ERR_FAIL_COND_MSG(skip_rpc && !(call_local_native || call_local_script), "RPC '" + p_method + "' on yourself is not allowed by selected mode."); + ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.sync, "RPC '" + p_method + "' on yourself is not allowed by selected mode."); } -void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) { - ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to RSET while no network peer is active."); - ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to RSET on a node which is not inside SceneTree."); - ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to send an RSET via a network peer which is not connected."); - - int node_id = network_peer->get_unique_id(); - bool is_master = p_node->is_network_master(); - bool skip_rset = node_id == p_peer_id; - bool set_local = false; - - if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { - // Check that send mode can use local call. - RPCMode rpc_mode = p_node->get_node_rset_mode(p_property); - set_local = _should_call_local(rpc_mode, is_master, skip_rset); - - if (set_local) { - bool valid; - int temp_id = rpc_sender_id; - - rpc_sender_id = get_network_unique_id(); - p_node->set(p_property, p_value, &valid); - rpc_sender_id = temp_id; - - if (!valid) { - String error = "rset() aborted in local set, property not found: - " + String(p_property) + "."; - ERR_PRINT(error); - return; - } - } else if (p_node->get_script_instance()) { - // Attempt with script. - rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); - - set_local = _should_call_local(rpc_mode, is_master, skip_rset); - - if (set_local) { - int temp_id = rpc_sender_id; - - rpc_sender_id = get_network_unique_id(); - bool valid = p_node->get_script_instance()->set(p_property, p_value); - rpc_sender_id = temp_id; - - if (!valid) { - String error = "rset() aborted in local script set, property not found: - " + String(p_property) + "."; - ERR_PRINT(error); - return; - } - } - } - } - - if (skip_rset) { - ERR_FAIL_COND_MSG(!set_local, "RSET for '" + p_property + "' on yourself is not allowed by selected mode."); - return; - } - -#ifdef DEBUG_ENABLED - _profile_node_data("out_rset", p_node->get_instance_id()); -#endif - - const Variant *vptr = &p_value; - - _send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1); -} - -Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) { +Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, MultiplayerPeer::TransferMode p_mode, int p_channel) { ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet."); ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active."); - ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected."); + ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected."); MAKE_ROOM(p_data.size() + 1); const uint8_t *r = p_data.ptr(); @@ -1142,6 +1002,7 @@ Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, NetworkedMult memcpy(&packet_cache.write[1], &r[0], p_data.size()); network_peer->set_target_peer(p_to); + network_peer->set_transfer_channel(p_channel); network_peer->set_transfer_mode(p_mode); return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); @@ -1157,7 +1018,37 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac uint8_t *w = out.ptrw(); memcpy(&w[0], &p_packet[1], len); } - emit_signal("network_peer_packet", p_from, out); + emit_signal(SNAME("network_peer_packet"), p_from, out); +} + +bool MultiplayerAPI::send_confirm_path(Node *p_node, NodePath p_path, int p_peer_id, int &r_id) { + // See if the path is cached. + PathSentCache *psc = path_send_cache.getptr(p_path); + if (!psc) { + // Path is not cached, create. + path_send_cache[p_path] = PathSentCache(); + psc = path_send_cache.getptr(p_path); + psc->id = last_send_cache_id++; + } + r_id = psc->id; + + // See if all peers have cached path (if so, call can be fast). + return _send_confirm_path(p_node, p_path, psc, p_peer_id); +} + +Node *MultiplayerAPI::get_cached_node(int p_from, uint32_t p_node_id) { + Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); + ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from)); + + Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(p_node_id); + ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_node_id, p_from)); + + PathGetCache::NodeInfo *ni = &F->get(); + Node *node = root_node->get_node(ni->path); + if (!node) { + ERR_PRINT("Failed to get cached path: " + String(ni->path) + "."); + } + return node; } int MultiplayerAPI::get_network_unique_id() const { @@ -1166,9 +1057,7 @@ int MultiplayerAPI::get_network_unique_id() const { } bool MultiplayerAPI::is_network_server() const { - // XXX Maybe fail silently? Maybe should actually return true to make development of both local and online multiplayer easier? - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. I can't be a server."); - return network_peer->is_server(); + return network_peer.is_valid() && network_peer->is_server(); } void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { @@ -1200,9 +1089,18 @@ bool MultiplayerAPI::is_object_decoding_allowed() const { return allow_object_decoding; } +MultiplayerReplicator *MultiplayerAPI::get_replicator() const { + return replicator; +} + +void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) { + replicator->scene_enter_exit_notify(p_scene, p_node, p_enter); +} + void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); - ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); + ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node); + ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer); ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer); ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id); @@ -1217,11 +1115,14 @@ void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections); ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding); ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed); + ClassDB::bind_method(D_METHOD("get_replicator"), &MultiplayerAPI::get_replicator); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_network_peer", "get_network_peer"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node"); ADD_PROPERTY_DEFAULT("refuse_new_network_connections", false); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replicator", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerReplicator", PROPERTY_USAGE_NONE), "", "get_replicator"); ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id"))); @@ -1234,15 +1135,14 @@ void MultiplayerAPI::_bind_methods() { BIND_ENUM_CONSTANT(RPC_MODE_REMOTE); BIND_ENUM_CONSTANT(RPC_MODE_MASTER); BIND_ENUM_CONSTANT(RPC_MODE_PUPPET); - BIND_ENUM_CONSTANT(RPC_MODE_REMOTESYNC); - BIND_ENUM_CONSTANT(RPC_MODE_MASTERSYNC); - BIND_ENUM_CONSTANT(RPC_MODE_PUPPETSYNC); } MultiplayerAPI::MultiplayerAPI() { + replicator = memnew(MultiplayerReplicator(this)); clear(); } MultiplayerAPI::~MultiplayerAPI() { clear(); + memdelete(replicator); } |