[MP] Allow multiple synchronizers per node.

The same node can now be controlled by multiple synchronizers at once.
Spawn visibility is composed in OR across multiple synchronizers (i.e.
if any synchronizer is visible to the peer, the object will be spawned).

Synchronizers controlling spawned nodes can now have a different
authority then the one spawning the node, in that case, the spawn
variables for that specific synchronizer are simply ignored (other
synchronizers with the same authority of the spawner will correctly
synchronize variables on spawn).

The SceneReplicationState class has been completly removed, since it was
bloating the implementation, the state is now simply stored in the
SceneReplicationInterface.
This commit is contained in:
Fabio Alessandrelli 2022-09-30 22:35:56 +02:00
parent 04a28763c0
commit 32a62af648
6 changed files with 409 additions and 565 deletions

View file

@ -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);

View file

@ -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();

View file

@ -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.
TrackedNode &tobj = _track(p_obj->get_instance_id());
const ObjectID sid = sync->get_instance_id();
tobj.synchronizers.insert(sid);
sync_nodes.insert(sid);
// 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);
}
// Update visibility.
sync->connect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed).bind(sync->get_instance_id()));
_update_sync_visibility(0, sync);
// 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);
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);
ERR_FAIL_COND_V(!node, ERR_BUG);
MultiplayerSpawner *spawner = rep_state->get_spawner(node->get_instance_id());
// 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);
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;

View file

@ -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;
_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;
// 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.
// An hack to apply the initial state before ready.
ObjectID pending_spawn;
const uint8_t *pending_buffer = nullptr;
int pending_buffer_size = 0;
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;
}
};

View file

@ -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;
}

View file

@ -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