diff options
Diffstat (limited to 'core/multiplayer/multiplayer_api.cpp')
-rw-r--r-- | core/multiplayer/multiplayer_api.cpp | 367 |
1 files changed, 144 insertions, 223 deletions
diff --git a/core/multiplayer/multiplayer_api.cpp b/core/multiplayer/multiplayer_api.cpp index 627825246a..e18c3dd2e4 100644 --- a/core/multiplayer/multiplayer_api.cpp +++ b/core/multiplayer/multiplayer_api.cpp @@ -32,9 +32,6 @@ #include "core/debugger/engine_debugger.h" #include "core/io/marshalls.h" -#include "core/multiplayer/multiplayer_replicator.h" -#include "core/multiplayer/rpc_manager.h" -#include "scene/main/node.h" #include <stdint.h> @@ -42,11 +39,14 @@ #include "core/os/os.h" #endif +MultiplayerReplicationInterface *(*MultiplayerAPI::create_default_replication_interface)(MultiplayerAPI *p_multiplayer) = nullptr; +MultiplayerRPCInterface *(*MultiplayerAPI::create_default_rpc_interface)(MultiplayerAPI *p_multiplayer) = nullptr; +MultiplayerCacheInterface *(*MultiplayerAPI::create_default_cache_interface)(MultiplayerAPI *p_multiplayer) = nullptr; + #ifdef DEBUG_ENABLED void MultiplayerAPI::profile_bandwidth(const String &p_inout, int p_size) { if (EngineDebugger::is_profiling("multiplayer")) { Array values; - values.push_back("bandwidth"); values.push_back(p_inout); values.push_back(OS::get_singleton()->get_ticks_msec()); values.push_back(p_size); @@ -74,7 +74,7 @@ void MultiplayerAPI::poll() { Error err = multiplayer_peer->get_packet(&packet, len); if (err != OK) { ERR_PRINT("Error getting packet!"); - break; // Something is wrong! + return; // Something is wrong! } remote_sender_id = sender; @@ -82,29 +82,25 @@ void MultiplayerAPI::poll() { remote_sender_id = 0; if (!multiplayer_peer.is_valid()) { - break; // It's also possible that a packet or RPC caused a disconnection, so also check here. + return; // It's also possible that a packet or RPC caused a disconnection, so also check here. } } - if (multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTED) { - replicator->poll(); - } + replicator->on_network_process(); } void MultiplayerAPI::clear() { - replicator->clear(); connected_peers.clear(); - path_get_cache.clear(); - path_send_cache.clear(); packet_cache.clear(); - last_send_cache_id = 1; + cache->clear(); } -void MultiplayerAPI::set_root_node(Node *p_node) { - root_node = p_node; +void MultiplayerAPI::set_root_path(const NodePath &p_path) { + ERR_FAIL_COND_MSG(!p_path.is_absolute() && !p_path.is_empty(), "MultiplayerAPI root path must be absolute."); + root_path = p_path; } -Node *MultiplayerAPI::get_root_node() { - return root_node; +NodePath MultiplayerAPI::get_root_path() const { + return root_path; } void MultiplayerAPI::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) { @@ -133,6 +129,7 @@ void MultiplayerAPI::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) { multiplayer_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); multiplayer_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); } + replicator->on_reset(); } Ref<MultiplayerPeer> MultiplayerAPI::get_multiplayer_peer() const { @@ -140,7 +137,7 @@ Ref<MultiplayerPeer> MultiplayerAPI::get_multiplayer_peer() const { } void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(root_node == nullptr, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it."); + ERR_FAIL_COND_MSG(root_path.is_empty(), "Multiplayer root was not initialized. If you are using custom multiplayer, remember to set the root path via MultiplayerAPI.set_root_path before using it."); ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small."); #ifdef DEBUG_ENABLED @@ -152,166 +149,32 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ switch (packet_type) { case NETWORK_COMMAND_SIMPLIFY_PATH: { - _process_simplify_path(p_from, p_packet, p_packet_len); + cache->process_simplify_path(p_from, p_packet, p_packet_len); } break; case NETWORK_COMMAND_CONFIRM_PATH: { - _process_confirm_path(p_from, p_packet, p_packet_len); + cache->process_confirm_path(p_from, p_packet, p_packet_len); } break; case NETWORK_COMMAND_REMOTE_CALL: { - rpc_manager->process_rpc(p_from, p_packet, p_packet_len); + rpc->process_rpc(p_from, p_packet, p_packet_len); } 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); + replicator->on_spawn_receive(p_from, p_packet, p_packet_len); } break; case NETWORK_COMMAND_DESPAWN: { - replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, false); + replicator->on_despawn_receive(p_from, p_packet, p_packet_len); } break; case NETWORK_COMMAND_SYNC: { - replicator->process_sync(p_from, p_packet, p_packet_len); + replicator->on_sync_receive(p_from, p_packet, p_packet_len); } break; } } -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; - - String methods_md5; - methods_md5.parse_utf8((const char *)(p_packet + ofs), 32); - ofs += 33; - - int id = decode_uint32(&p_packet[ofs]); - ofs += 4; - - String paths; - paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs); - - NodePath path = paths; - - if (!path_get_cache.has(p_from)) { - path_get_cache[p_from] = PathGetCache(); - } - - Node *node = root_node->get_node(path); - ERR_FAIL_COND(node == nullptr); - const bool valid_rpc_checksum = rpc_manager->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); - } - - PathGetCache::NodeInfo ni; - ni.path = path; - - path_get_cache[p_from].nodes[id] = ni; - - // Encode path to send ack. - CharString pname = String(path).utf8(); - int len = encode_cstring(pname.get_data(), nullptr); - - Vector<uint8_t> packet; - - packet.resize(1 + 1 + len); - packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH; - packet.write[1] = valid_rpc_checksum; - encode_cstring(pname.get_data(), &packet.write[2]); - - multiplayer_peer->set_transfer_channel(0); - multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - multiplayer_peer->set_target_peer(p_from); - multiplayer_peer->put_packet(packet.ptr(), packet.size()); -} - -void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small."); - - const bool valid_rpc_checksum = p_packet[1]; - - String paths; - paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2); - - NodePath path = paths; - - 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); - } - - PathSentCache *psc = path_send_cache.getptr(path); - ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache."); - - Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from); - ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path."); - E->get() = true; -} - -bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) { - bool has_all_peers = true; - List<int> peers_to_add; // If one is missing, take note to add it. - - for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { - if (p_target < 0 && E->get() == -p_target) { - continue; // Continue, excluded. - } - - if (p_target > 0 && E->get() != p_target) { - continue; // Continue, not for this peer. - } - - Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); - - if (!F || !F->get()) { - // Path was not cached, or was cached but is unconfirmed. - if (!F) { - // Not cached at all, take note. - peers_to_add.push_back(E->get()); - } - - has_all_peers = false; - } - } - - if (peers_to_add.size() > 0) { - // Those that need to be added, send a message for this. - - // Encode function name. - const CharString path = String(p_path).utf8(); - const int path_len = encode_cstring(path.get_data(), nullptr); - - // Extract MD5 from rpc methods list. - const String methods_md5 = rpc_manager->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; - packet.resize(1 + 4 + path_len + methods_md5_len); - int ofs = 0; - - packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH; - ofs += 1; - - ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]); - - ofs += encode_uint32(psc->id, &packet.write[ofs]); - - ofs += encode_cstring(path.get_data(), &packet.write[ofs]); - - for (int &E : peers_to_add) { - multiplayer_peer->set_target_peer(E); // To all of you. - multiplayer_peer->set_transfer_channel(0); - multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - multiplayer_peer->put_packet(packet.ptr(), packet.size()); - - psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed. - } - } - - return has_all_peers; -} - // The variant is compressed and encoded; The first byte contains all the meta // information and the format is: // - The first LSB 5 bits are used for the variant type. @@ -324,7 +187,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, bool p_allow_object_decoding) { // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31 CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK); @@ -385,7 +248,7 @@ Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint } break; default: // Any other case is not yet compressed. - Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding); + Error err = encode_variant(p_variant, r_buffer, r_len, p_allow_object_decoding); if (err != OK) { return err; } @@ -399,7 +262,7 @@ Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint 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, bool p_allow_object_decoding) { const uint8_t *buf = p_buffer; int len = p_len; @@ -458,7 +321,7 @@ Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const ui } } break; default: - Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding); + Error err = decode_variant(r_variant, p_buffer, p_len, r_len, p_allow_object_decoding); if (err != OK) { return err; } @@ -467,27 +330,86 @@ Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const ui return OK; } +Error MultiplayerAPI::encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw, bool p_allow_object_decoding) { + r_len = 0; + int size = 0; + + if (p_count == 0) { + if (r_raw) { + *r_raw = true; + } + return OK; + } + + // Try raw encoding optimization. + if (r_raw && p_count == 1) { + *r_raw = false; + const Variant &v = *(p_variants[0]); + if (v.get_type() == Variant::PACKED_BYTE_ARRAY) { + *r_raw = true; + const PackedByteArray pba = v; + if (p_buffer) { + memcpy(p_buffer, pba.ptr(), pba.size()); + } + r_len += pba.size(); + } else { + encode_and_compress_variant(v, p_buffer, size, p_allow_object_decoding); + r_len += size; + } + return OK; + } + + // Regular encoding. + for (int i = 0; i < p_count; i++) { + const Variant &v = *(p_variants[i]); + encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size, p_allow_object_decoding); + r_len += size; + } + return OK; +} + +Error MultiplayerAPI::decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw, bool p_allow_object_decoding) { + r_len = 0; + int argc = r_variants.size(); + if (argc == 0 && p_raw) { + return OK; + } + ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA); + if (p_raw) { + r_len = p_len; + PackedByteArray pba; + pba.resize(p_len); + memcpy(pba.ptrw(), p_buffer, p_len); + r_variants.write[0] = pba; + return OK; + } + + Vector<Variant> args; + Vector<const Variant *> argp; + args.resize(argc); + + for (int i = 0; i < argc; i++) { + ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small."); + + int vlen; + Error err = MultiplayerAPI::decode_and_decompress_variant(r_variants.write[i], &p_buffer[r_len], p_len - r_len, &vlen, p_allow_object_decoding); + ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable."); + r_len += vlen; + } + return OK; +} + void MultiplayerAPI::_add_peer(int p_id) { connected_peers.insert(p_id); - path_get_cache.insert(p_id, PathGetCache()); - if (is_server()) { - replicator->spawn_all(p_id); - } + cache->on_peer_change(p_id, true); + replicator->on_peer_change(p_id, true); emit_signal(SNAME("peer_connected"), p_id); } void MultiplayerAPI::_del_peer(int p_id) { + replicator->on_peer_change(p_id, false); + cache->on_peer_change(p_id, false); connected_peers.erase(p_id); - // Cleanup get cache. - path_get_cache.erase(p_id); - // Cleanup sent cache. - // Some refactoring is needed to make this faster and do paths GC. - List<NodePath> keys; - path_send_cache.get_key_list(&keys); - for (const NodePath &E : keys) { - PathSentCache *psc = path_send_cache.getptr(E); - psc->confirmed_peers.erase(p_id); - } emit_signal(SNAME("peer_disconnected"), p_id); } @@ -500,6 +422,7 @@ void MultiplayerAPI::_connection_failed() { } void MultiplayerAPI::_server_disconnected() { + replicator->on_reset(); emit_signal(SNAME("server_disconnected")); } @@ -537,41 +460,15 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac } bool MultiplayerAPI::is_cache_confirmed(NodePath p_path, int p_peer) { - const PathSentCache *psc = path_send_cache.getptr(p_path); - ERR_FAIL_COND_V(!psc, false); - const Map<int, bool>::Element *F = psc->confirmed_peers.find(p_peer); - ERR_FAIL_COND_V(!F, false); // Should never happen. - return F->get(); -} - -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); + return cache->is_cache_confirmed(p_path, p_peer); } -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)); +bool MultiplayerAPI::send_object_cache(Object *p_obj, NodePath p_path, int p_peer_id, int &r_id) { + return cache->send_object_cache(p_obj, p_path, p_peer_id, r_id); +} - 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; +Object *MultiplayerAPI::get_cached_object(int p_from, uint32_t p_cache_id) { + return cache->get_cached_object(p_from, p_cache_id); } int MultiplayerAPI::get_unique_id() const { @@ -597,7 +494,7 @@ Vector<int> MultiplayerAPI::get_peer_ids() const { ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), Vector<int>(), "No multiplayer peer is assigned. Assume no peers are connected."); Vector<int> ret; - for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { + for (RBSet<int>::Element *E = connected_peers.front(); E; E = E->next()) { ret.push_back(E->get()); } @@ -612,17 +509,33 @@ bool MultiplayerAPI::is_object_decoding_allowed() const { return allow_object_decoding; } -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); +String MultiplayerAPI::get_rpc_md5(const Object *p_obj) const { + return rpc->get_rpc_md5(p_obj); } -void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { - rpc_manager->rpcp(p_node, p_peer_id, p_method, p_arg, p_argcount); +void MultiplayerAPI::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { + rpc->rpcp(p_obj, p_peer_id, p_method, p_arg, p_argcount); +} + +Error MultiplayerAPI::spawn(Object *p_object, Variant p_config) { + return replicator->on_spawn(p_object, p_config); +} + +Error MultiplayerAPI::despawn(Object *p_object, Variant p_config) { + return replicator->on_despawn(p_object, p_config); +} + +Error MultiplayerAPI::replication_start(Object *p_object, Variant p_config) { + return replicator->on_replication_start(p_object, p_config); +} + +Error MultiplayerAPI::replication_stop(Object *p_object, Variant p_config) { + return replicator->on_replication_stop(p_object, p_config); } void MultiplayerAPI::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); - ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node); + ClassDB::bind_method(D_METHOD("set_root_path", "path"), &MultiplayerAPI::set_root_path); + ClassDB::bind_method(D_METHOD("get_root_path"), &MultiplayerAPI::get_root_path); ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); ClassDB::bind_method(D_METHOD("has_multiplayer_peer"), &MultiplayerAPI::has_multiplayer_peer); ClassDB::bind_method(D_METHOD("get_multiplayer_peer"), &MultiplayerAPI::get_multiplayer_peer); @@ -638,14 +551,12 @@ void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerAPI::is_refusing_new_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_connections"), "set_refuse_new_connections", "is_refusing_new_connections"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path"); ADD_PROPERTY_DEFAULT("refuse_new_connections", false); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replicator", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerReplicator", PROPERTY_USAGE_NONE), "", "get_replicator"); ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id"))); @@ -656,13 +567,23 @@ void MultiplayerAPI::_bind_methods() { } MultiplayerAPI::MultiplayerAPI() { - replicator = memnew(MultiplayerReplicator(this)); - rpc_manager = memnew(RPCManager(this)); - clear(); + if (create_default_replication_interface) { + replicator = Ref<MultiplayerReplicationInterface>(create_default_replication_interface(this)); + } else { + replicator.instantiate(); + } + if (create_default_rpc_interface) { + rpc = Ref<MultiplayerRPCInterface>(create_default_rpc_interface(this)); + } else { + rpc.instantiate(); + } + if (create_default_cache_interface) { + cache = Ref<MultiplayerCacheInterface>(create_default_cache_interface(this)); + } else { + cache.instantiate(); + } } MultiplayerAPI::~MultiplayerAPI() { clear(); - memdelete(replicator); - memdelete(rpc_manager); } |