Browse Source

Merge pull request #66794 from Faless/mp/4.x_multiple_sync

[MP] Allow multiple synchronizers per node.
Fabio Alessandrelli 2 years ago
parent
commit
c477e7c461

+ 41 - 5
modules/multiplayer/multiplayer_synchronizer.cpp

@@ -48,6 +48,8 @@ void MultiplayerSynchronizer::_stop() {
 		return;
 	}
 #endif
+	root_node_cache = ObjectID();
+	reset();
 	Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
 	if (node) {
 		get_multiplayer()->object_configuration_remove(node, this);
@@ -60,8 +62,11 @@ void MultiplayerSynchronizer::_start() {
 		return;
 	}
 #endif
+	root_node_cache = ObjectID();
+	reset();
 	Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
 	if (node) {
+		root_node_cache = node->get_instance_id();
 		get_multiplayer()->object_configuration_add(node, this);
 		_update_process();
 	}
@@ -94,6 +99,40 @@ void MultiplayerSynchronizer::_update_process() {
 	}
 }
 
+Node *MultiplayerSynchronizer::get_root_node() {
+	return root_node_cache.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(root_node_cache)) : nullptr;
+}
+
+void MultiplayerSynchronizer::reset() {
+	net_id = 0;
+	last_sync_msec = 0;
+	last_inbound_sync = 0;
+}
+
+uint32_t MultiplayerSynchronizer::get_net_id() const {
+	return net_id;
+}
+
+void MultiplayerSynchronizer::set_net_id(uint32_t p_net_id) {
+	net_id = p_net_id;
+}
+
+bool MultiplayerSynchronizer::update_outbound_sync_time(uint64_t p_msec) {
+	if (p_msec >= last_sync_msec + interval_msec) {
+		last_sync_msec = p_msec;
+		return true;
+	}
+	return false;
+}
+
+bool MultiplayerSynchronizer::update_inbound_sync_time(uint16_t p_network_time) {
+	if (p_network_time <= last_inbound_sync && last_inbound_sync - p_network_time < 32767) {
+		return false;
+	}
+	last_inbound_sync = p_network_time;
+	return true;
+}
+
 PackedStringArray MultiplayerSynchronizer::get_configuration_warnings() const {
 	PackedStringArray warnings = Node::get_configuration_warnings();
 
@@ -263,10 +302,6 @@ double MultiplayerSynchronizer::get_replication_interval() const {
 	return double(interval_msec) / 1000.0;
 }
 
-uint64_t MultiplayerSynchronizer::get_replication_interval_msec() const {
-	return interval_msec;
-}
-
 void MultiplayerSynchronizer::set_replication_config(Ref<SceneReplicationConfig> p_config) {
 	replication_config = p_config;
 }
@@ -299,10 +334,11 @@ NodePath MultiplayerSynchronizer::get_root_path() const {
 
 void MultiplayerSynchronizer::set_multiplayer_authority(int p_peer_id, bool p_recursive) {
 	Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
-	if (!node) {
+	if (!node || get_multiplayer_authority() == p_peer_id) {
 		Node::set_multiplayer_authority(p_peer_id, p_recursive);
 		return;
 	}
+
 	get_multiplayer()->object_configuration_remove(node, this);
 	Node::set_multiplayer_authority(p_peer_id, p_recursive);
 	get_multiplayer()->object_configuration_add(node, this);

+ 14 - 1
modules/multiplayer/multiplayer_synchronizer.h

@@ -53,6 +53,11 @@ private:
 	HashSet<Callable> visibility_filters;
 	HashSet<int> peer_visibility;
 
+	ObjectID root_node_cache;
+	uint64_t last_sync_msec = 0;
+	uint16_t last_inbound_sync = 0;
+	uint32_t net_id = 0;
+
 	static Object *_get_prop_target(Object *p_obj, const NodePath &p_prop);
 	void _start();
 	void _stop();
@@ -66,11 +71,19 @@ public:
 	static Error get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs);
 	static Error set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state);
 
+	void reset();
+	Node *get_root_node();
+
+	uint32_t get_net_id() const;
+	void set_net_id(uint32_t p_net_id);
+
+	bool update_outbound_sync_time(uint64_t p_msec);
+	bool update_inbound_sync_time(uint16_t p_network_time);
+
 	PackedStringArray get_configuration_warnings() const override;
 
 	void set_replication_interval(double p_interval);
 	double get_replication_interval() const;
-	uint64_t get_replication_interval_msec() const;
 
 	void set_replication_config(Ref<SceneReplicationConfig> p_config);
 	Ref<SceneReplicationConfig> get_replication_config();

+ 296 - 142
modules/multiplayer/scene_replication_interface.cpp

@@ -30,21 +30,47 @@
 
 #include "scene_replication_interface.h"
 
+#include "scene_multiplayer.h"
+
 #include "core/io/marshalls.h"
 #include "scene/main/node.h"
-
-#include "multiplayer_spawner.h"
-#include "multiplayer_synchronizer.h"
-#include "scene_multiplayer.h"
+#include "scene/scene_string_names.h"
 
 #define MAKE_ROOM(m_amount)             \
 	if (packet_cache.size() < m_amount) \
 		packet_cache.resize(m_amount);
 
-void SceneReplicationInterface::_free_remotes(int p_id) {
-	const HashMap<uint32_t, ObjectID> remotes = rep_state->peer_get_remotes(p_id);
-	for (const KeyValue<uint32_t, ObjectID> &E : remotes) {
-		Node *node = rep_state->get_node(E.value);
+SceneReplicationInterface::TrackedNode &SceneReplicationInterface::_track(const ObjectID &p_id) {
+	if (!tracked_nodes.has(p_id)) {
+		tracked_nodes[p_id] = TrackedNode(p_id);
+		Node *node = get_id_as<Node>(p_id);
+		node->connect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationInterface::_untrack).bind(p_id), Node::CONNECT_ONE_SHOT);
+	}
+	return tracked_nodes[p_id];
+}
+
+void SceneReplicationInterface::_untrack(const ObjectID &p_id) {
+	if (!tracked_nodes.has(p_id)) {
+		return;
+	}
+	uint32_t net_id = tracked_nodes[p_id].net_id;
+	uint32_t peer = tracked_nodes[p_id].remote_peer;
+	tracked_nodes.erase(p_id);
+	// If it was spawned by a remote, remove it from the received nodes.
+	if (peer && peers_info.has(peer)) {
+		peers_info[peer].recv_nodes.erase(net_id);
+	}
+	// If we spawned or synced it, we need to remove it from any peer it was sent to.
+	if (net_id || peer == 0) {
+		for (KeyValue<int, PeerInfo> &E : peers_info) {
+			E.value.spawn_nodes.erase(p_id);
+		}
+	}
+}
+
+void SceneReplicationInterface::_free_remotes(const PeerInfo &p_info) {
+	for (const KeyValue<uint32_t, ObjectID> &E : p_info.recv_nodes) {
+		Node *node = tracked_nodes.has(E.value) ? get_id_as<Node>(E.value) : nullptr;
 		ERR_CONTINUE(!node);
 		node->queue_delete();
 	}
@@ -52,34 +78,48 @@ void SceneReplicationInterface::_free_remotes(int p_id) {
 
 void SceneReplicationInterface::on_peer_change(int p_id, bool p_connected) {
 	if (p_connected) {
-		rep_state->on_peer_change(p_id, p_connected);
-		for (const ObjectID &oid : rep_state->get_spawned_nodes()) {
+		peers_info[p_id] = PeerInfo();
+		for (const ObjectID &oid : spawned_nodes) {
 			_update_spawn_visibility(p_id, oid);
 		}
-		for (const ObjectID &oid : rep_state->get_synced_nodes()) {
-			MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
-			ERR_CONTINUE(!sync); // ERR_BUG
-			if (sync->is_multiplayer_authority()) {
-				_update_sync_visibility(p_id, oid);
-			}
+		for (const ObjectID &oid : sync_nodes) {
+			_update_sync_visibility(p_id, get_id_as<MultiplayerSynchronizer>(oid));
 		}
 	} else {
-		_free_remotes(p_id);
-		rep_state->on_peer_change(p_id, p_connected);
+		ERR_FAIL_COND(!peers_info.has(p_id));
+		_free_remotes(peers_info[p_id]);
+		peers_info.erase(p_id);
 	}
 }
 
 void SceneReplicationInterface::on_reset() {
-	for (int pid : rep_state->get_peers()) {
-		_free_remotes(pid);
+	for (const KeyValue<int, PeerInfo> &E : peers_info) {
+		_free_remotes(E.value);
 	}
-	rep_state->reset();
+	peers_info.clear();
+	// Tracked nodes are cleared on deletion, here we only reset the ids so they can be later re-assigned.
+	for (KeyValue<ObjectID, TrackedNode> &E : tracked_nodes) {
+		TrackedNode &tobj = E.value;
+		tobj.net_id = 0;
+		tobj.remote_peer = 0;
+	}
+	for (const ObjectID &oid : sync_nodes) {
+		MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(oid);
+		ERR_CONTINUE(!sync);
+		sync->reset();
+	}
+	last_net_id = 0;
 }
 
 void SceneReplicationInterface::on_network_process() {
 	uint64_t msec = OS::get_singleton()->get_ticks_msec();
-	for (int peer : rep_state->get_peers()) {
-		_send_sync(peer, msec);
+	for (KeyValue<int, PeerInfo> &E : peers_info) {
+		const HashSet<ObjectID> to_sync = E.value.sync_nodes;
+		if (to_sync.is_empty()) {
+			continue; // Nothing to sync
+		}
+		uint16_t sync_net_time = ++E.value.last_sent_sync;
+		_send_sync(E.key, to_sync, sync_net_time, msec);
 	}
 }
 
@@ -88,14 +128,19 @@ Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) {
 	ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
 	MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object());
 	ERR_FAIL_COND_V(!spawner, ERR_INVALID_PARAMETER);
-	Error err = rep_state->config_add_spawn(node, spawner);
-	ERR_FAIL_COND_V(err != OK, err);
+	// Track node.
 	const ObjectID oid = node->get_instance_id();
+	TrackedNode &tobj = _track(oid);
+	ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE);
+	tobj.spawner = spawner->get_instance_id();
+	spawned_nodes.insert(oid);
+
 	if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
-		rep_state->ensure_net_id(oid);
+		if (tobj.net_id == 0) {
+			tobj.net_id = ++last_net_id;
+		}
 		_update_spawn_visibility(0, oid);
 	}
-	ERR_FAIL_COND_V(err != OK, err);
 	return OK;
 }
 
@@ -109,14 +154,22 @@ Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
 	Error err = _make_despawn_packet(node, len);
 	ERR_FAIL_COND_V(err != OK, ERR_BUG);
 	const ObjectID oid = p_obj->get_instance_id();
-	for (int pid : rep_state->get_peers()) {
-		if (!rep_state->is_peer_spawn(pid, oid)) {
+	for (const KeyValue<int, PeerInfo> &E : peers_info) {
+		if (!E.value.spawn_nodes.has(oid)) {
 			continue;
 		}
-		_send_raw(packet_cache.ptr(), len, pid, true);
+		_send_raw(packet_cache.ptr(), len, E.key, true);
 	}
 	// Also remove spawner tracking from the replication state.
-	return rep_state->config_del_spawn(node, spawner);
+	ERR_FAIL_COND_V(!tracked_nodes.has(oid), ERR_INVALID_PARAMETER);
+	TrackedNode &tobj = _track(oid);
+	ERR_FAIL_COND_V(tobj.spawner != spawner->get_instance_id(), ERR_INVALID_PARAMETER);
+	tobj.spawner = ObjectID();
+	spawned_nodes.erase(oid);
+	for (KeyValue<int, PeerInfo> &E : peers_info) {
+		E.value.spawn_nodes.erase(oid);
+	}
+	return OK;
 }
 
 Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_config) {
@@ -125,28 +178,40 @@ Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_c
 	MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
 	ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
 
-	const ObjectID oid = node->get_instance_id();
-	MultiplayerSpawner *spawner = rep_state->get_spawner(oid);
-	ERR_FAIL_COND_V_MSG(spawner && spawner->get_multiplayer_authority() != sync->get_multiplayer_authority(), ERR_INVALID_PARAMETER, "The authority of the MultiplayerSynchronizer \"" + String(sync->get_path()) + "\" differs from the authority of its \"root_node\" spawner and will not sync. Change the \"root_node\" of the MultiplayerSynchronizer to be a child of the scene root instead.");
-
-	// Add to synchronizer list and setup visibility.
-	rep_state->config_add_sync(node, sync);
-	sync->connect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed).bind(oid));
-	if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) {
-		_update_sync_visibility(0, oid);
-	}
-
-	// Try to apply initial state if spawning (hack to apply if before ready).
-	if (pending_spawn == p_obj->get_instance_id()) {
-		pending_spawn = ObjectID(); // Make sure this only happens once.
-		const List<NodePath> props = sync->get_replication_config()->get_spawn_properties();
-		Vector<Variant> vars;
-		vars.resize(props.size());
-		int consumed;
-		Error err = MultiplayerAPI::decode_and_decompress_variants(vars, pending_buffer, pending_buffer_size, consumed);
-		ERR_FAIL_COND_V(err, err);
-		err = MultiplayerSynchronizer::set_state(props, node, vars);
-		ERR_FAIL_COND_V(err, err);
+	// Add to synchronizer list.
+	TrackedNode &tobj = _track(p_obj->get_instance_id());
+	const ObjectID sid = sync->get_instance_id();
+	tobj.synchronizers.insert(sid);
+	sync_nodes.insert(sid);
+
+	// Update visibility.
+	sync->connect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed).bind(sync->get_instance_id()));
+	_update_sync_visibility(0, sync);
+
+	if (pending_spawn == p_obj->get_instance_id() && sync->get_multiplayer_authority() == pending_spawn_remote) {
+		// Try to apply synchronizer Net ID
+		ERR_FAIL_COND_V_MSG(pending_sync_net_ids.is_empty(), ERR_INVALID_DATA, vformat("The MultiplayerSynchronizer at path \"%s\" is unable to process the pending spawn since it has no network ID. This might happen when changing the multiplayer authority during the \"_ready\" callback. Make sure to only change the authority of multiplayer synchronizers during \"_enter_tree\" or the \"_spawn_custom\" callback of their multiplayer spawner.", sync->get_path()));
+		ERR_FAIL_COND_V(!peers_info.has(pending_spawn_remote), ERR_INVALID_DATA);
+		uint32_t net_id = pending_sync_net_ids[0];
+		pending_sync_net_ids.pop_front();
+		peers_info[pending_spawn_remote].recv_sync_ids[net_id] = sync->get_instance_id();
+
+		// Try to apply spawn state (before ready).
+		if (pending_buffer_size > 0) {
+			ERR_FAIL_COND_V(!node || sync->get_replication_config().is_null(), ERR_UNCONFIGURED);
+			int consumed = 0;
+			const List<NodePath> props = sync->get_replication_config()->get_spawn_properties();
+			Vector<Variant> vars;
+			vars.resize(props.size());
+			Error err = MultiplayerAPI::decode_and_decompress_variants(vars, pending_buffer, pending_buffer_size, consumed);
+			ERR_FAIL_COND_V(err, err);
+			if (consumed > 0) {
+				pending_buffer += consumed;
+				pending_buffer_size -= consumed;
+				err = MultiplayerSynchronizer::set_state(props, node, vars);
+				ERR_FAIL_COND_V(err, err);
+			}
+		}
 	}
 	return OK;
 }
@@ -157,59 +222,98 @@ Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_co
 	MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
 	ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
 	sync->disconnect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed));
-	return rep_state->config_del_sync(node, sync);
+	// Untrack synchronizer.
+	const ObjectID oid = node->get_instance_id();
+	const ObjectID sid = sync->get_instance_id();
+	ERR_FAIL_COND_V(!tracked_nodes.has(oid), ERR_INVALID_PARAMETER);
+	TrackedNode &tobj = _track(oid);
+	tobj.synchronizers.erase(sid);
+	sync_nodes.erase(sid);
+	for (KeyValue<int, PeerInfo> &E : peers_info) {
+		E.value.sync_nodes.erase(sid);
+		if (sync->get_net_id()) {
+			E.value.recv_sync_ids.erase(sync->get_net_id());
+		}
+	}
+	return OK;
 }
 
-void SceneReplicationInterface::_visibility_changed(int p_peer, ObjectID p_oid) {
-	if (rep_state->is_spawned_node(p_oid)) {
-		_update_spawn_visibility(p_peer, p_oid);
-	}
-	if (rep_state->is_synced_node(p_oid)) {
-		_update_sync_visibility(p_peer, p_oid);
+void SceneReplicationInterface::_visibility_changed(int p_peer, ObjectID p_sid) {
+	MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(p_sid);
+	ERR_FAIL_COND(!sync); // Bug.
+	Node *node = sync->get_root_node();
+	ERR_FAIL_COND(!node); // Bug.
+	const ObjectID oid = node->get_instance_id();
+	if (spawned_nodes.has(oid)) {
+		_update_spawn_visibility(p_peer, oid);
 	}
+	_update_sync_visibility(p_peer, sync);
 }
 
-Error SceneReplicationInterface::_update_sync_visibility(int p_peer, const ObjectID &p_oid) {
-	MultiplayerSynchronizer *sync = rep_state->get_synchronizer(p_oid);
-	ERR_FAIL_COND_V(!sync || !sync->is_multiplayer_authority(), ERR_BUG);
-	bool is_visible = sync->is_visible_to(p_peer);
+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()) {
+		return OK;
+	}
+
+	const ObjectID &sid = p_sync->get_instance_id();
+	bool is_visible = p_sync->is_visible_to(p_peer);
 	if (p_peer == 0) {
-		for (int pid : rep_state->get_peers()) {
+		for (KeyValue<int, PeerInfo> &E : peers_info) {
 			// Might be visible to this specific peer.
-			is_visible = is_visible || sync->is_visible_to(pid);
-			if (rep_state->is_peer_sync(pid, p_oid) == is_visible) {
+			is_visible = is_visible || p_sync->is_visible_to(E.key);
+			if (is_visible == E.value.sync_nodes.has(sid)) {
 				continue;
 			}
 			if (is_visible) {
-				rep_state->peer_add_sync(pid, p_oid);
+				E.value.sync_nodes.insert(sid);
 			} else {
-				rep_state->peer_del_sync(pid, p_oid);
+				E.value.sync_nodes.erase(sid);
 			}
 		}
 		return OK;
 	} else {
-		if (is_visible == rep_state->is_peer_sync(p_peer, p_oid)) {
+		ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+		if (is_visible == peers_info[p_peer].sync_nodes.has(sid)) {
 			return OK;
 		}
 		if (is_visible) {
-			return rep_state->peer_add_sync(p_peer, p_oid);
+			peers_info[p_peer].sync_nodes.insert(sid);
 		} else {
-			return rep_state->peer_del_sync(p_peer, p_oid);
+			peers_info[p_peer].sync_nodes.erase(sid);
 		}
+		return OK;
 	}
 }
 
 Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const ObjectID &p_oid) {
-	MultiplayerSpawner *spawner = rep_state->get_spawner(p_oid);
-	MultiplayerSynchronizer *sync = rep_state->get_synchronizer(p_oid);
-	Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_oid));
+	const TrackedNode *tnode = tracked_nodes.getptr(p_oid);
+	ERR_FAIL_COND_V(!tnode, ERR_BUG);
+	MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tnode->spawner);
+	Node *node = get_id_as<Node>(p_oid);
 	ERR_FAIL_COND_V(!node || !spawner || !spawner->is_multiplayer_authority(), ERR_BUG);
-	bool is_visible = !sync || sync->is_visible_to(p_peer);
+	ERR_FAIL_COND_V(!tracked_nodes.has(p_oid), ERR_BUG);
+	const HashSet<ObjectID> synchronizers = tracked_nodes[p_oid].synchronizers;
+	bool is_visible = true;
+	for (const ObjectID &sid : synchronizers) {
+		MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(sid);
+		ERR_CONTINUE(!sync);
+		if (!sync->is_multiplayer_authority()) {
+			continue;
+		}
+		// Spawn visibility is composed using OR when multiple synchronizers are present.
+		if (sync->is_visible_to(p_peer)) {
+			is_visible = true;
+			break;
+		}
+		is_visible = false;
+	}
 	// Spawn (and despawn) when needed.
 	HashSet<int> to_spawn;
 	HashSet<int> to_despawn;
 	if (p_peer) {
-		if (is_visible == rep_state->is_peer_spawn(p_peer, p_oid)) {
+		ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+		if (is_visible == peers_info[p_peer].spawn_nodes.has(p_oid)) {
 			return OK;
 		}
 		if (is_visible) {
@@ -219,33 +323,37 @@ Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const Obje
 		}
 	} else {
 		// Check visibility for each peers.
-		for (int pid : rep_state->get_peers()) {
-			bool peer_visible = is_visible || sync->is_visible_to(pid);
-			if (peer_visible == rep_state->is_peer_spawn(pid, p_oid)) {
-				continue;
-			}
-			if (peer_visible) {
-				to_spawn.insert(pid);
+		for (const KeyValue<int, PeerInfo> &E : peers_info) {
+			if (is_visible) {
+				// This is fast, since the the object is visibile to everyone, we don't need to check each peer.
+				if (E.value.spawn_nodes.has(p_oid)) {
+					// Already spawned.
+					continue;
+				}
+				to_spawn.insert(E.key);
 			} else {
-				to_despawn.insert(pid);
+				// Need to check visibility for each peer.
+				_update_spawn_visibility(E.key, p_oid);
 			}
 		}
 	}
 	if (to_spawn.size()) {
 		int len = 0;
-		_make_spawn_packet(node, len);
+		_make_spawn_packet(node, spawner, len);
 		for (int pid : to_spawn) {
+			ERR_CONTINUE(!peers_info.has(pid));
 			int path_id;
 			multiplayer->get_path_cache()->send_object_cache(spawner, pid, path_id);
 			_send_raw(packet_cache.ptr(), len, pid, true);
-			rep_state->peer_add_spawn(pid, p_oid);
+			peers_info[pid].spawn_nodes.insert(p_oid);
 		}
 	}
 	if (to_despawn.size()) {
 		int len = 0;
 		_make_despawn_packet(node, len);
 		for (int pid : to_despawn) {
-			rep_state->peer_del_spawn(pid, p_oid);
+			ERR_CONTINUE(!peers_info.has(pid));
+			peers_info[pid].spawn_nodes.erase(p_oid);
 			_send_raw(packet_cache.ptr(), len, pid, true);
 		}
 	}
@@ -268,20 +376,20 @@ Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size,
 	return peer->put_packet(p_buffer, p_size);
 }
 
-Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, int &r_len) {
-	ERR_FAIL_COND_V(!multiplayer, ERR_BUG);
+Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len) {
+	ERR_FAIL_COND_V(!multiplayer || !p_node || !p_spawner, ERR_BUG);
 
 	const ObjectID oid = p_node->get_instance_id();
-	MultiplayerSpawner *spawner = rep_state->get_spawner(oid);
-	ERR_FAIL_COND_V(!spawner || !p_node, ERR_BUG);
+	const TrackedNode *tnode = tracked_nodes.getptr(oid);
+	ERR_FAIL_COND_V(!tnode, ERR_INVALID_PARAMETER);
 
-	uint32_t nid = rep_state->get_net_id(oid);
+	uint32_t nid = tnode->net_id;
 	ERR_FAIL_COND_V(!nid, ERR_UNCONFIGURED);
 
 	// Prepare custom arg and scene_id
-	uint8_t scene_id = spawner->find_spawnable_scene_index_from_object(oid);
+	uint8_t scene_id = p_spawner->find_spawnable_scene_index_from_object(oid);
 	bool is_custom = scene_id == MultiplayerSpawner::INVALID_ID;
-	Variant spawn_arg = spawner->get_spawn_argument(oid);
+	Variant spawn_arg = p_spawner->get_spawn_argument(oid);
 	int spawn_arg_size = 0;
 	if (is_custom) {
 		Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, nullptr, spawn_arg_size, false);
@@ -289,31 +397,51 @@ Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, int &r_len) {
 	}
 
 	// Prepare spawn state.
+	List<NodePath> state_props;
+	List<uint32_t> sync_ids;
+	const HashSet<ObjectID> synchronizers = tnode->synchronizers;
+	for (const ObjectID &sid : synchronizers) {
+		MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(sid);
+		if (!sync->is_multiplayer_authority()) {
+			continue;
+		}
+		ERR_CONTINUE(!sync);
+		ERR_FAIL_COND_V(sync->get_replication_config().is_null(), ERR_BUG);
+		for (const NodePath &prop : sync->get_replication_config()->get_spawn_properties()) {
+			state_props.push_back(prop);
+		}
+		// Ensure the synchronizer has an ID.
+		if (sync->get_net_id() == 0) {
+			sync->set_net_id(++last_net_id);
+		}
+		sync_ids.push_back(sync->get_net_id());
+	}
 	int state_size = 0;
 	Vector<Variant> state_vars;
 	Vector<const Variant *> state_varp;
-	MultiplayerSynchronizer *synchronizer = rep_state->get_synchronizer(oid);
-	if (synchronizer) {
-		ERR_FAIL_COND_V(synchronizer->get_replication_config().is_null(), ERR_BUG);
-		const List<NodePath> props = synchronizer->get_replication_config()->get_spawn_properties();
-		Error err = MultiplayerSynchronizer::get_state(props, p_node, state_vars, state_varp);
+	if (state_props.size()) {
+		Error err = MultiplayerSynchronizer::get_state(state_props, p_node, state_vars, state_varp);
 		ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to retrieve spawn state.");
 		err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), nullptr, state_size);
 		ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to encode spawn state.");
 	}
 
 	// Encode scene ID, path ID, net ID, node name.
-	int path_id = multiplayer->get_path_cache()->make_object_cache(spawner);
+	int path_id = multiplayer->get_path_cache()->make_object_cache(p_spawner);
 	CharString cname = p_node->get_name().operator String().utf8();
 	int nlen = encode_cstring(cname.get_data(), nullptr);
-	MAKE_ROOM(1 + 1 + 4 + 4 + 4 + nlen + (is_custom ? 4 + spawn_arg_size : 0) + state_size);
+	MAKE_ROOM(1 + 1 + 4 + 4 + 4 + 4 * sync_ids.size() + 4 + nlen + (is_custom ? 4 + spawn_arg_size : 0) + state_size);
 	uint8_t *ptr = packet_cache.ptrw();
 	ptr[0] = (uint8_t)SceneMultiplayer::NETWORK_COMMAND_SPAWN;
 	ptr[1] = scene_id;
 	int ofs = 2;
 	ofs += encode_uint32(path_id, &ptr[ofs]);
 	ofs += encode_uint32(nid, &ptr[ofs]);
+	ofs += encode_uint32(sync_ids.size(), &ptr[ofs]);
 	ofs += encode_uint32(nlen, &ptr[ofs]);
+	for (uint32_t snid : sync_ids) {
+		ofs += encode_uint32(snid, &ptr[ofs]);
+	}
 	ofs += encode_cstring(cname.get_data(), &ptr[ofs]);
 	// Write args
 	if (is_custom) {
@@ -334,18 +462,20 @@ Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, int &r_len) {
 
 Error SceneReplicationInterface::_make_despawn_packet(Node *p_node, int &r_len) {
 	const ObjectID oid = p_node->get_instance_id();
+	const TrackedNode *tnode = tracked_nodes.getptr(oid);
+	ERR_FAIL_COND_V(!tnode, ERR_INVALID_PARAMETER);
 	MAKE_ROOM(5);
 	uint8_t *ptr = packet_cache.ptrw();
 	ptr[0] = (uint8_t)SceneMultiplayer::NETWORK_COMMAND_DESPAWN;
 	int ofs = 1;
-	uint32_t nid = rep_state->get_net_id(oid);
+	uint32_t nid = tnode->net_id;
 	ofs += encode_uint32(nid, &ptr[ofs]);
 	r_len = ofs;
 	return OK;
 }
 
 Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
-	ERR_FAIL_COND_V_MSG(p_buffer_len < 14, ERR_INVALID_DATA, "Invalid spawn packet received");
+	ERR_FAIL_COND_V_MSG(p_buffer_len < 18, ERR_INVALID_DATA, "Invalid spawn packet received");
 	int ofs = 1; // The spawn/despawn command.
 	uint8_t scene_id = p_buffer[ofs];
 	ofs += 1;
@@ -357,9 +487,16 @@ Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_b
 
 	uint32_t net_id = decode_uint32(&p_buffer[ofs]);
 	ofs += 4;
+	uint32_t sync_len = decode_uint32(&p_buffer[ofs]);
+	ofs += 4;
 	uint32_t name_len = decode_uint32(&p_buffer[ofs]);
 	ofs += 4;
-	ERR_FAIL_COND_V_MSG(name_len > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA, vformat("Invalid spawn packet size: %d, wants: %d", p_buffer_len, ofs + name_len));
+	ERR_FAIL_COND_V_MSG(name_len + (sync_len * 4) > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA, vformat("Invalid spawn packet size: %d, wants: %d", p_buffer_len, ofs + name_len + (sync_len * 4)));
+	List<uint32_t> sync_ids;
+	for (uint32_t i = 0; i < sync_len; i++) {
+		sync_ids.push_back(decode_uint32(&p_buffer[ofs]));
+		ofs += 4;
+	}
 	ERR_FAIL_COND_V_MSG(name_len < 1, ERR_INVALID_DATA, "Zero spawn name size.");
 
 	// We need to make sure no trickery happens here, but we want to allow autogenerated ("@") node names.
@@ -390,20 +527,35 @@ Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_b
 	}
 	ERR_FAIL_COND_V(!node, ERR_UNAUTHORIZED);
 	node->set_name(name);
-	rep_state->peer_add_remote(p_from, net_id, node, spawner);
+
+	// Add and track remote
+	ERR_FAIL_COND_V(!peers_info.has(p_from), ERR_UNAVAILABLE);
+	ERR_FAIL_COND_V(peers_info[p_from].recv_nodes.has(net_id), ERR_ALREADY_IN_USE);
+	ObjectID oid = node->get_instance_id();
+	TrackedNode &tobj = _track(oid);
+	tobj.spawner = spawner->get_instance_id();
+	tobj.net_id = net_id;
+	tobj.remote_peer = p_from;
+	peers_info[p_from].recv_nodes[net_id] = oid;
+
 	// The initial state will be applied during the sync config (i.e. before _ready).
-	int state_len = p_buffer_len - ofs;
-	if (state_len) {
-		pending_spawn = node->get_instance_id();
-		pending_buffer = &p_buffer[ofs];
-		pending_buffer_size = state_len;
-	}
+	pending_spawn = node->get_instance_id();
+	pending_spawn_remote = p_from;
+	pending_buffer_size = p_buffer_len - ofs;
+	pending_buffer = pending_buffer_size > 0 ? &p_buffer[ofs] : nullptr;
+	pending_sync_net_ids = sync_ids;
+
 	parent->add_child(node);
 	spawner->emit_signal(SNAME("spawned"), node);
 
 	pending_spawn = ObjectID();
+	pending_spawn_remote = 0;
 	pending_buffer = nullptr;
 	pending_buffer_size = 0;
+	if (pending_sync_net_ids.size()) {
+		pending_sync_net_ids.clear();
+		ERR_FAIL_V(ERR_INVALID_DATA); // Should have been consumed.
+	}
 	return OK;
 }
 
@@ -412,12 +564,18 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p
 	int ofs = 1; // The spawn/despawn command.
 	uint32_t net_id = decode_uint32(&p_buffer[ofs]);
 	ofs += 4;
-	Node *node = nullptr;
-	Error err = rep_state->peer_del_remote(p_from, net_id, &node);
-	ERR_FAIL_COND_V(err != OK, err);
+
+	// Untrack remote
+	ERR_FAIL_COND_V(!peers_info.has(p_from), ERR_UNAUTHORIZED);
+	PeerInfo &pinfo = peers_info[p_from];
+	ERR_FAIL_COND_V(!pinfo.recv_nodes.has(net_id), ERR_UNAUTHORIZED);
+	Node *node = get_id_as<Node>(pinfo.recv_nodes[net_id]);
 	ERR_FAIL_COND_V(!node, ERR_BUG);
+	pinfo.recv_nodes.erase(net_id);
 
-	MultiplayerSpawner *spawner = rep_state->get_spawner(node->get_instance_id());
+	const ObjectID oid = node->get_instance_id();
+	ERR_FAIL_COND_V(!tracked_nodes.has(oid), ERR_BUG);
+	MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tracked_nodes[oid].spawner);
 	ERR_FAIL_COND_V(!spawner, ERR_DOES_NOT_EXIST);
 	ERR_FAIL_COND_V(p_from != spawner->get_multiplayer_authority(), ERR_UNAUTHORIZED);
 
@@ -430,27 +588,24 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p
 	return OK;
 }
 
-void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
-	const HashSet<ObjectID> &to_sync = rep_state->get_peer_sync_nodes(p_peer);
-	if (to_sync.is_empty()) {
-		return;
-	}
+void SceneReplicationInterface::_send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_msec) {
 	MAKE_ROOM(sync_mtu);
 	uint8_t *ptr = packet_cache.ptrw();
 	ptr[0] = SceneMultiplayer::NETWORK_COMMAND_SYNC;
 	int ofs = 1;
-	ofs += encode_uint16(rep_state->peer_sync_next(p_peer), &ptr[1]);
+	ofs += encode_uint16(p_sync_net_time, &ptr[1]);
 	// Can only send updates for already notified nodes.
 	// This is a lazy implementation, we could optimize much more here with by grouping by replication config.
-	for (const ObjectID &oid : to_sync) {
-		if (!rep_state->update_sync_time(oid, p_msec)) {
+	for (const ObjectID &oid : p_synchronizers) {
+		MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(oid);
+		ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid() || !sync->is_multiplayer_authority());
+		if (!sync->update_outbound_sync_time(p_msec)) {
 			continue; // nothing to sync.
 		}
-		MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
-		ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid());
-		Node *node = rep_state->get_node(oid);
+
+		Node *node = sync->get_root_node();
 		ERR_CONTINUE(!node);
-		uint32_t net_id = rep_state->get_net_id(oid);
+		uint32_t net_id = sync->get_net_id();
 		if (net_id == 0 || (net_id & 0x80000000)) {
 			int path_id = 0;
 			bool verified = multiplayer->get_path_cache()->send_object_cache(sync, p_peer, path_id);
@@ -458,7 +613,7 @@ void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
 			if (net_id == 0) {
 				// First time path based ID.
 				net_id = path_id | 0x80000000;
-				rep_state->set_net_id(oid, net_id | 0x80000000);
+				sync->set_net_id(net_id | 0x80000000);
 			}
 			if (!verified) {
 				// The path based sync is not yet confirmed, skipping.
@@ -481,7 +636,7 @@ void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
 			ofs = 3;
 		}
 		if (size) {
-			ofs += encode_uint32(rep_state->get_net_id(oid), &ptr[ofs]);
+			ofs += encode_uint32(sync->get_net_id(), &ptr[ofs]);
 			ofs += encode_uint32(size, &ptr[ofs]);
 			MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), &ptr[ofs], size);
 			ofs += size;
@@ -497,33 +652,32 @@ Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_bu
 	ERR_FAIL_COND_V_MSG(p_buffer_len < 11, ERR_INVALID_DATA, "Invalid sync packet received");
 	uint16_t time = decode_uint16(&p_buffer[1]);
 	int ofs = 3;
-	rep_state->peer_sync_recv(p_from, time);
 	while (ofs + 8 < p_buffer_len) {
 		uint32_t net_id = decode_uint32(&p_buffer[ofs]);
 		ofs += 4;
 		uint32_t size = decode_uint32(&p_buffer[ofs]);
 		ofs += 4;
-		Node *node = nullptr;
+		MultiplayerSynchronizer *sync = nullptr;
 		if (net_id & 0x80000000) {
-			MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_path_cache()->get_cached_object(p_from, net_id & 0x7FFFFFFF));
-			ERR_FAIL_COND_V(!sync || sync->get_multiplayer_authority() != p_from, ERR_UNAUTHORIZED);
-			node = sync->get_node(sync->get_root_path());
-		} else {
-			node = rep_state->peer_get_remote(p_from, net_id);
+			sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_path_cache()->get_cached_object(p_from, net_id & 0x7FFFFFFF));
+		} else if (peers_info[p_from].recv_sync_ids.has(net_id)) {
+			const ObjectID &sid = peers_info[p_from].recv_sync_ids[net_id];
+			sync = get_id_as<MultiplayerSynchronizer>(sid);
 		}
-		if (!node) {
+		if (!sync) {
 			// Not received yet.
 			ofs += size;
 			continue;
 		}
-		const ObjectID oid = node->get_instance_id();
-		if (!rep_state->update_last_node_sync(oid, time)) {
+		Node *node = sync->get_root_node();
+		if (sync->get_multiplayer_authority() != p_from || !node) {
+			ERR_CONTINUE(true);
+		}
+		if (!sync->update_inbound_sync_time(time)) {
 			// State is too old.
 			ofs += size;
 			continue;
 		}
-		MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
-		ERR_FAIL_COND_V(!sync, ERR_BUG);
 		ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_BUG);
 		const List<NodePath> props = sync->get_replication_config()->get_sync_properties();
 		Vector<Variant> vars;

+ 59 - 16
modules/multiplayer/scene_replication_interface.h

@@ -31,9 +31,10 @@
 #ifndef SCENE_REPLICATION_INTERFACE_H
 #define SCENE_REPLICATION_INTERFACE_H
 
-#include "scene/main/multiplayer_api.h"
+#include "core/object/ref_counted.h"
 
-#include "scene_replication_state.h"
+#include "multiplayer_spawner.h"
+#include "multiplayer_synchronizer.h"
 
 class SceneMultiplayer;
 
@@ -41,25 +42,68 @@ class SceneReplicationInterface : public RefCounted {
 	GDCLASS(SceneReplicationInterface, RefCounted);
 
 private:
-	void _send_sync(int p_peer, uint64_t p_msec);
-	Error _make_spawn_packet(Node *p_node, int &r_len);
-	Error _make_despawn_packet(Node *p_node, int &r_len);
-	Error _send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable);
+	struct TrackedNode {
+		ObjectID id;
+		uint32_t net_id = 0;
+		uint32_t remote_peer = 0;
+		ObjectID spawner;
+		HashSet<ObjectID> synchronizers;
 
-	void _visibility_changed(int p_peer, ObjectID p_oid);
-	Error _update_sync_visibility(int p_peer, const ObjectID &p_oid);
-	Error _update_spawn_visibility(int p_peer, const ObjectID &p_oid);
-	void _free_remotes(int p_peer);
+		bool operator==(const ObjectID &p_other) { return id == p_other; }
 
-	Ref<SceneReplicationState> rep_state;
-	SceneMultiplayer *multiplayer = nullptr;
-	PackedByteArray packet_cache;
-	int sync_mtu = 1350; // Highly dependent on underlying protocol.
+		_FORCE_INLINE_ MultiplayerSpawner *get_spawner() const { return spawner.is_valid() ? Object::cast_to<MultiplayerSpawner>(ObjectDB::get_instance(spawner)) : nullptr; }
+		TrackedNode() {}
+		TrackedNode(const ObjectID &p_id) { id = p_id; }
+		TrackedNode(const ObjectID &p_id, uint32_t p_net_id) {
+			id = p_id;
+			net_id = p_net_id;
+		}
+	};
+
+	struct PeerInfo {
+		HashSet<ObjectID> sync_nodes;
+		HashSet<ObjectID> spawn_nodes;
+		HashMap<uint32_t, ObjectID> recv_sync_ids;
+		HashMap<uint32_t, ObjectID> recv_nodes;
+		uint16_t last_sent_sync = 0;
+	};
+
+	// Replication state.
+	HashMap<int, PeerInfo> peers_info;
+	uint32_t last_net_id = 0;
+	HashMap<ObjectID, TrackedNode> tracked_nodes;
+	HashSet<ObjectID> spawned_nodes;
+	HashSet<ObjectID> sync_nodes;
 
-	// An hack to apply the initial state before ready.
+	// Pending spawn informations.
 	ObjectID pending_spawn;
+	int pending_spawn_remote = 0;
 	const uint8_t *pending_buffer = nullptr;
 	int pending_buffer_size = 0;
+	List<uint32_t> pending_sync_net_ids;
+
+	// Replicator config.
+	SceneMultiplayer *multiplayer = nullptr;
+	PackedByteArray packet_cache;
+	int sync_mtu = 1350; // Highly dependent on underlying protocol.
+
+	TrackedNode &_track(const ObjectID &p_id);
+	void _untrack(const ObjectID &p_id);
+
+	void _send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_msec);
+	Error _make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len);
+	Error _make_despawn_packet(Node *p_node, int &r_len);
+	Error _send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable);
+
+	void _visibility_changed(int p_peer, ObjectID p_oid);
+	Error _update_sync_visibility(int p_peer, MultiplayerSynchronizer *p_sync);
+	Error _update_spawn_visibility(int p_peer, const ObjectID &p_oid);
+	void _free_remotes(const PeerInfo &p_info);
+
+	template <class T>
+	static T *get_id_as(const ObjectID &p_id) {
+		return p_id.is_valid() ? Object::cast_to<T>(ObjectDB::get_instance(p_id)) : nullptr;
+	}
 
 public:
 	static void make_default();
@@ -78,7 +122,6 @@ public:
 	Error on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len);
 
 	SceneReplicationInterface(SceneMultiplayer *p_multiplayer) {
-		rep_state.instantiate();
 		multiplayer = p_multiplayer;
 	}
 };

+ 0 - 267
modules/multiplayer/scene_replication_state.cpp

@@ -1,267 +0,0 @@
-/*************************************************************************/
-/*  scene_replication_state.cpp                                          */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2022 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       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#include "scene_replication_state.h"
-
-#include "scene/scene_string_names.h"
-
-#include "multiplayer_spawner.h"
-#include "multiplayer_synchronizer.h"
-
-SceneReplicationState::TrackedNode &SceneReplicationState::_track(const ObjectID &p_id) {
-	if (!tracked_nodes.has(p_id)) {
-		tracked_nodes[p_id] = TrackedNode(p_id);
-		Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id));
-		node->connect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack).bind(p_id), Node::CONNECT_ONE_SHOT);
-	}
-	return tracked_nodes[p_id];
-}
-
-void SceneReplicationState::_untrack(const ObjectID &p_id) {
-	if (tracked_nodes.has(p_id)) {
-		uint32_t net_id = tracked_nodes[p_id].net_id;
-		uint32_t peer = tracked_nodes[p_id].remote_peer;
-		tracked_nodes.erase(p_id);
-		// If it was spawned by a remote, remove it from the received nodes.
-		if (peer && peers_info.has(peer)) {
-			peers_info[peer].recv_nodes.erase(net_id);
-		}
-		// If we spawned or synced it, we need to remove it from any peer it was sent to.
-		if (net_id || peer == 0) {
-			for (KeyValue<int, PeerInfo> &E : peers_info) {
-				E.value.sync_nodes.erase(p_id);
-				E.value.spawn_nodes.erase(p_id);
-			}
-		}
-	}
-}
-
-const HashMap<uint32_t, ObjectID> SceneReplicationState::peer_get_remotes(int p_peer) const {
-	return peers_info.has(p_peer) ? peers_info[p_peer].recv_nodes : HashMap<uint32_t, ObjectID>();
-}
-
-bool SceneReplicationState::update_last_node_sync(const ObjectID &p_id, uint16_t p_time) {
-	TrackedNode *tnode = tracked_nodes.getptr(p_id);
-	ERR_FAIL_COND_V(!tnode, false);
-	if (p_time <= tnode->last_sync && tnode->last_sync - p_time < 32767) {
-		return false;
-	}
-	tnode->last_sync = p_time;
-	return true;
-}
-
-bool SceneReplicationState::update_sync_time(const ObjectID &p_id, uint64_t p_msec) {
-	TrackedNode *tnode = tracked_nodes.getptr(p_id);
-	ERR_FAIL_COND_V(!tnode, false);
-	MultiplayerSynchronizer *sync = get_synchronizer(p_id);
-	if (!sync) {
-		return false;
-	}
-	if (tnode->last_sync_msec == p_msec) {
-		return true;
-	}
-	if (p_msec >= tnode->last_sync_msec + sync->get_replication_interval_msec()) {
-		tnode->last_sync_msec = p_msec;
-		return true;
-	}
-	return false;
-}
-
-uint32_t SceneReplicationState::get_net_id(const ObjectID &p_id) const {
-	const TrackedNode *tnode = tracked_nodes.getptr(p_id);
-	ERR_FAIL_COND_V(!tnode, 0);
-	return tnode->net_id;
-}
-
-void SceneReplicationState::set_net_id(const ObjectID &p_id, uint32_t p_net_id) {
-	TrackedNode *tnode = tracked_nodes.getptr(p_id);
-	ERR_FAIL_COND(!tnode);
-	tnode->net_id = p_net_id;
-}
-
-uint32_t SceneReplicationState::ensure_net_id(const ObjectID &p_id) {
-	TrackedNode *tnode = tracked_nodes.getptr(p_id);
-	ERR_FAIL_COND_V(!tnode, 0);
-	if (tnode->net_id == 0) {
-		tnode->net_id = ++last_net_id;
-	}
-	return tnode->net_id;
-}
-
-void SceneReplicationState::on_peer_change(int p_peer, bool p_connected) {
-	if (p_connected) {
-		peers_info[p_peer] = PeerInfo();
-		known_peers.insert(p_peer);
-	} else {
-		peers_info.erase(p_peer);
-		known_peers.erase(p_peer);
-	}
-}
-
-void SceneReplicationState::reset() {
-	peers_info.clear();
-	known_peers.clear();
-	// Tracked nodes are cleared on deletion, here we only reset the ids so they can be later re-assigned.
-	for (KeyValue<ObjectID, TrackedNode> &E : tracked_nodes) {
-		TrackedNode &tobj = E.value;
-		tobj.net_id = 0;
-		tobj.remote_peer = 0;
-		tobj.last_sync = 0;
-	}
-}
-
-Error SceneReplicationState::config_add_spawn(Node *p_node, MultiplayerSpawner *p_spawner) {
-	const ObjectID oid = p_node->get_instance_id();
-	TrackedNode &tobj = _track(oid);
-	ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE);
-	tobj.spawner = p_spawner->get_instance_id();
-	spawned_nodes.insert(oid);
-	return OK;
-}
-
-Error SceneReplicationState::config_del_spawn(Node *p_node, MultiplayerSpawner *p_spawner) {
-	const ObjectID oid = p_node->get_instance_id();
-	ERR_FAIL_COND_V(!is_tracked(oid), ERR_INVALID_PARAMETER);
-	TrackedNode &tobj = _track(oid);
-	ERR_FAIL_COND_V(tobj.spawner != p_spawner->get_instance_id(), ERR_INVALID_PARAMETER);
-	tobj.spawner = ObjectID();
-	spawned_nodes.erase(oid);
-	for (KeyValue<int, PeerInfo> &E : peers_info) {
-		E.value.spawn_nodes.erase(oid);
-	}
-	return OK;
-}
-
-Error SceneReplicationState::config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync) {
-	const ObjectID oid = p_node->get_instance_id();
-	TrackedNode &tobj = _track(oid);
-	ERR_FAIL_COND_V(tobj.synchronizer != ObjectID(), ERR_ALREADY_IN_USE);
-	tobj.synchronizer = p_sync->get_instance_id();
-	synced_nodes.insert(oid);
-	return OK;
-}
-
-Error SceneReplicationState::config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync) {
-	const ObjectID oid = p_node->get_instance_id();
-	ERR_FAIL_COND_V(!is_tracked(oid), ERR_INVALID_PARAMETER);
-	TrackedNode &tobj = _track(oid);
-	ERR_FAIL_COND_V(tobj.synchronizer != p_sync->get_instance_id(), ERR_INVALID_PARAMETER);
-	tobj.synchronizer = ObjectID();
-	synced_nodes.erase(oid);
-	for (KeyValue<int, PeerInfo> &E : peers_info) {
-		E.value.sync_nodes.erase(oid);
-	}
-	return OK;
-}
-
-Error SceneReplicationState::peer_add_sync(int p_peer, const ObjectID &p_id) {
-	ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
-	peers_info[p_peer].sync_nodes.insert(p_id);
-	return OK;
-}
-
-Error SceneReplicationState::peer_del_sync(int p_peer, const ObjectID &p_id) {
-	ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
-	peers_info[p_peer].sync_nodes.erase(p_id);
-	return OK;
-}
-
-const HashSet<ObjectID> SceneReplicationState::get_peer_sync_nodes(int p_peer) {
-	ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
-	return peers_info[p_peer].sync_nodes;
-}
-
-bool SceneReplicationState::is_peer_sync(int p_peer, const ObjectID &p_id) const {
-	ERR_FAIL_COND_V(!peers_info.has(p_peer), false);
-	return peers_info[p_peer].sync_nodes.has(p_id);
-}
-
-Error SceneReplicationState::peer_add_spawn(int p_peer, const ObjectID &p_id) {
-	ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
-	peers_info[p_peer].spawn_nodes.insert(p_id);
-	return OK;
-}
-
-Error SceneReplicationState::peer_del_spawn(int p_peer, const ObjectID &p_id) {
-	ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
-	peers_info[p_peer].spawn_nodes.erase(p_id);
-	return OK;
-}
-
-const HashSet<ObjectID> SceneReplicationState::get_peer_spawn_nodes(int p_peer) {
-	ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
-	return peers_info[p_peer].spawn_nodes;
-}
-
-bool SceneReplicationState::is_peer_spawn(int p_peer, const ObjectID &p_id) const {
-	ERR_FAIL_COND_V(!peers_info.has(p_peer), false);
-	return peers_info[p_peer].spawn_nodes.has(p_id);
-}
-
-Node *SceneReplicationState::peer_get_remote(int p_peer, uint32_t p_net_id) {
-	PeerInfo *info = peers_info.getptr(p_peer);
-	return info && info->recv_nodes.has(p_net_id) ? Object::cast_to<Node>(ObjectDB::get_instance(info->recv_nodes[p_net_id])) : nullptr;
-}
-
-Error SceneReplicationState::peer_add_remote(int p_peer, uint32_t p_net_id, Node *p_node, MultiplayerSpawner *p_spawner) {
-	ERR_FAIL_COND_V(!p_node || !p_spawner, ERR_INVALID_PARAMETER);
-	ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_UNAVAILABLE);
-	PeerInfo &pinfo = peers_info[p_peer];
-	ObjectID oid = p_node->get_instance_id();
-	TrackedNode &tobj = _track(oid);
-	tobj.spawner = p_spawner->get_instance_id();
-	tobj.net_id = p_net_id;
-	tobj.remote_peer = p_peer;
-	tobj.last_sync = pinfo.last_recv_sync;
-	// Also track as a remote.
-	ERR_FAIL_COND_V(pinfo.recv_nodes.has(p_net_id), ERR_ALREADY_IN_USE);
-	pinfo.recv_nodes[p_net_id] = oid;
-	return OK;
-}
-
-Error SceneReplicationState::peer_del_remote(int p_peer, uint32_t p_net_id, Node **r_node) {
-	ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_UNAUTHORIZED);
-	PeerInfo &info = peers_info[p_peer];
-	ERR_FAIL_COND_V(!info.recv_nodes.has(p_net_id), ERR_UNAUTHORIZED);
-	*r_node = Object::cast_to<Node>(ObjectDB::get_instance(info.recv_nodes[p_net_id]));
-	info.recv_nodes.erase(p_net_id);
-	return OK;
-}
-
-uint16_t SceneReplicationState::peer_sync_next(int p_peer) {
-	ERR_FAIL_COND_V(!peers_info.has(p_peer), 0);
-	PeerInfo &info = peers_info[p_peer];
-	return ++info.last_sent_sync;
-}
-
-void SceneReplicationState::peer_sync_recv(int p_peer, uint16_t p_time) {
-	ERR_FAIL_COND(!peers_info.has(p_peer));
-	peers_info[p_peer].last_recv_sync = p_time;
-}

+ 0 - 135
modules/multiplayer/scene_replication_state.h

@@ -1,135 +0,0 @@
-/*************************************************************************/
-/*  scene_replication_state.h                                            */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2022 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       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#ifndef SCENE_REPLICATION_STATE_H
-#define SCENE_REPLICATION_STATE_H
-
-#include "core/object/ref_counted.h"
-
-#include "multiplayer_spawner.h"
-#include "multiplayer_synchronizer.h"
-
-class MultiplayerSpawner;
-class MultiplayerSynchronizer;
-class Node;
-
-class SceneReplicationState : public RefCounted {
-private:
-	struct TrackedNode {
-		ObjectID id;
-		uint32_t net_id = 0;
-		uint32_t remote_peer = 0;
-		ObjectID spawner;
-		ObjectID synchronizer;
-		uint16_t last_sync = 0;
-		uint64_t last_sync_msec = 0;
-
-		bool operator==(const ObjectID &p_other) { return id == p_other; }
-
-		Node *get_node() const { return id.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(id)) : nullptr; }
-		MultiplayerSpawner *get_spawner() const { return spawner.is_valid() ? Object::cast_to<MultiplayerSpawner>(ObjectDB::get_instance(spawner)) : nullptr; }
-		MultiplayerSynchronizer *get_synchronizer() const { return synchronizer.is_valid() ? Object::cast_to<MultiplayerSynchronizer>(ObjectDB::get_instance(synchronizer)) : nullptr; }
-		TrackedNode() {}
-		TrackedNode(const ObjectID &p_id) { id = p_id; }
-		TrackedNode(const ObjectID &p_id, uint32_t p_net_id) {
-			id = p_id;
-			net_id = p_net_id;
-		}
-	};
-
-	struct PeerInfo {
-		HashSet<ObjectID> sync_nodes;
-		HashSet<ObjectID> spawn_nodes;
-		HashMap<uint32_t, ObjectID> recv_nodes;
-		uint16_t last_sent_sync = 0;
-		uint16_t last_recv_sync = 0;
-	};
-
-	HashSet<int> known_peers;
-	uint32_t last_net_id = 0;
-	HashMap<ObjectID, TrackedNode> tracked_nodes;
-	HashMap<int, PeerInfo> peers_info;
-	HashSet<ObjectID> spawned_nodes;
-	HashSet<ObjectID> synced_nodes;
-
-	TrackedNode &_track(const ObjectID &p_id);
-	void _untrack(const ObjectID &p_id);
-	bool is_tracked(const ObjectID &p_id) const { return tracked_nodes.has(p_id); }
-
-public:
-	const HashSet<int> get_peers() const { return known_peers; }
-	const HashSet<ObjectID> &get_spawned_nodes() const { return spawned_nodes; }
-	bool is_spawned_node(const ObjectID &p_id) const { return spawned_nodes.has(p_id); }
-	const HashSet<ObjectID> &get_synced_nodes() const { return synced_nodes; }
-	bool is_synced_node(const ObjectID &p_id) const { return synced_nodes.has(p_id); }
-
-	MultiplayerSynchronizer *get_synchronizer(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_synchronizer() : nullptr; }
-	MultiplayerSpawner *get_spawner(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_spawner() : nullptr; }
-	Node *get_node(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_node() : nullptr; }
-	bool update_last_node_sync(const ObjectID &p_id, uint16_t p_time);
-	bool update_sync_time(const ObjectID &p_id, uint64_t p_msec);
-
-	uint32_t get_net_id(const ObjectID &p_id) const;
-	void set_net_id(const ObjectID &p_id, uint32_t p_net_id);
-	uint32_t ensure_net_id(const ObjectID &p_id);
-
-	void reset();
-	void on_peer_change(int p_peer, bool p_connected);
-
-	Error config_add_spawn(Node *p_node, MultiplayerSpawner *p_spawner);
-	Error config_del_spawn(Node *p_node, MultiplayerSpawner *p_spawner);
-
-	Error config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
-	Error config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
-
-	Error peer_add_sync(int p_peer, const ObjectID &p_id);
-	Error peer_del_sync(int p_peer, const ObjectID &p_id);
-
-	const HashSet<ObjectID> get_peer_sync_nodes(int p_peer);
-	bool is_peer_sync(int p_peer, const ObjectID &p_id) const;
-
-	Error peer_add_spawn(int p_peer, const ObjectID &p_id);
-	Error peer_del_spawn(int p_peer, const ObjectID &p_id);
-
-	const HashSet<ObjectID> get_peer_spawn_nodes(int p_peer);
-	bool is_peer_spawn(int p_peer, const ObjectID &p_id) const;
-
-	const HashMap<uint32_t, ObjectID> peer_get_remotes(int p_peer) const;
-	Node *peer_get_remote(int p_peer, uint32_t p_net_id);
-	Error peer_add_remote(int p_peer, uint32_t p_net_id, Node *p_node, MultiplayerSpawner *p_spawner);
-	Error peer_del_remote(int p_peer, uint32_t p_net_id, Node **r_node);
-
-	uint16_t peer_sync_next(int p_peer);
-	void peer_sync_recv(int p_peer, uint16_t p_time);
-
-	SceneReplicationState() {}
-};
-
-#endif // SCENE_REPLICATION_STATE_H