1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-07 12:30:27 +00:00

[Net] New replication interface, spawner and synchronizer nodes.

Initial implementation of the MultiplayerReplicationInterface and its
default implementation (SceneReplicationInterface).

New MultiplayerSpawner node helps dealing with instantiation of scenes
on remote peers (e.g. clients).
It supports both custom spawns via a `_spawn_custom` virtual function,
and optional auto-spawn of known scenes via a TypedArray<PackedScenes>
property.

New MultiplayerSynchornizer helps synchronizing states between the local
and remote peers, supports both sync and spawn properties and is
configured via a `SceneReplicationConfig` resource.
It can also sync via path (i.e. without being spawned by a
MultiplayerSpawner if both peers has it in tree, but will not send the
spawn state in that case, only the sync one.
This commit is contained in:
Fabio Alessandrelli
2021-10-08 14:13:06 +02:00
parent 2e320dcf87
commit d219547c96
29 changed files with 2011 additions and 1212 deletions

View File

@@ -32,7 +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"
@@ -42,6 +41,8 @@
#include "core/os/os.h"
#endif
MultiplayerReplicationInterface *(*MultiplayerAPI::create_default_replication_interface)(MultiplayerAPI *p_multiplayer) = nullptr;
#ifdef DEBUG_ENABLED
void MultiplayerAPI::profile_bandwidth(const String &p_inout, int p_size) {
if (EngineDebugger::is_profiling("multiplayer")) {
@@ -74,7 +75,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,16 +83,13 @@ 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();
@@ -133,6 +131,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 {
@@ -167,13 +166,13 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
_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;
}
}
@@ -324,7 +323,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 +384,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 +398,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 +457,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,17 +466,84 @@ 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);
}
replicator->on_peer_change(p_id, true);
emit_signal(SNAME("peer_connected"), p_id);
}
void MultiplayerAPI::_del_peer(int p_id) {
connected_peers.erase(p_id);
replicator->on_peer_change(p_id, false);
// Cleanup get cache.
path_get_cache.erase(p_id);
// Cleanup sent cache.
@@ -488,6 +554,7 @@ void MultiplayerAPI::_del_peer(int p_id) {
PathSentCache *psc = path_send_cache.getptr(E);
psc->confirmed_peers.erase(p_id);
}
connected_peers.erase(p_id);
emit_signal(SNAME("peer_disconnected"), p_id);
}
@@ -500,6 +567,7 @@ void MultiplayerAPI::_connection_failed() {
}
void MultiplayerAPI::_server_disconnected() {
replicator->on_reset();
emit_signal(SNAME("server_disconnected"));
}
@@ -612,14 +680,26 @@ 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);
}
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);
}
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);
@@ -638,14 +718,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_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 +734,16 @@ void MultiplayerAPI::_bind_methods() {
}
MultiplayerAPI::MultiplayerAPI() {
replicator = memnew(MultiplayerReplicator(this));
if (create_default_replication_interface) {
replicator = Ref<MultiplayerReplicationInterface>(create_default_replication_interface(this));
} else {
replicator.instantiate();
}
rpc_manager = memnew(RPCManager(this));
clear();
}
MultiplayerAPI::~MultiplayerAPI() {
clear();
memdelete(replicator);
memdelete(rpc_manager);
}