Merge pull request #62961 from Faless/mp/4.x_interest
Add peer visibility to MultiplayerSynchronizer.
This commit is contained in:
commit
1cf7ebda50
15 changed files with 482 additions and 128 deletions
|
@ -463,8 +463,12 @@ bool MultiplayerAPI::is_cache_confirmed(NodePath p_path, int p_peer) {
|
|||
return cache->is_cache_confirmed(p_path, p_peer);
|
||||
}
|
||||
|
||||
bool MultiplayerAPI::send_object_cache(Object *p_obj, NodePath p_path, int p_peer_id, int &r_id) {
|
||||
return cache->send_object_cache(p_obj, p_path, p_peer_id, r_id);
|
||||
bool MultiplayerAPI::send_object_cache(Object *p_obj, int p_peer_id, int &r_id) {
|
||||
return cache->send_object_cache(p_obj, p_peer_id, r_id);
|
||||
}
|
||||
|
||||
int MultiplayerAPI::make_object_cache(Object *p_obj) {
|
||||
return cache->make_object_cache(p_obj);
|
||||
}
|
||||
|
||||
Object *MultiplayerAPI::get_cached_object(int p_from, uint32_t p_cache_id) {
|
||||
|
|
|
@ -77,7 +77,8 @@ public:
|
|||
virtual void process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {}
|
||||
|
||||
// Returns true if all peers have cached path.
|
||||
virtual bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id) { return false; }
|
||||
virtual bool send_object_cache(Object *p_obj, int p_target, int &r_id) { return false; }
|
||||
virtual int make_object_cache(Object *p_obj) { return false; }
|
||||
virtual Object *get_cached_object(int p_from, uint32_t p_cache_id) { return nullptr; }
|
||||
virtual bool is_cache_confirmed(NodePath p_path, int p_peer) { return false; }
|
||||
|
||||
|
@ -160,7 +161,8 @@ public:
|
|||
Error replication_start(Object *p_object, Variant p_config);
|
||||
Error replication_stop(Object *p_object, Variant p_config);
|
||||
// Cache API
|
||||
bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id);
|
||||
bool send_object_cache(Object *p_obj, int p_target, int &r_id);
|
||||
int make_object_cache(Object *p_obj);
|
||||
Object *get_cached_object(int p_from, uint32_t p_cache_id);
|
||||
bool is_cache_confirmed(NodePath p_path, int p_peer);
|
||||
|
||||
|
|
|
@ -43,8 +43,6 @@
|
|||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="auto_spawn" type="bool" setter="set_auto_spawning" getter="is_auto_spawning" default="false">
|
||||
</member>
|
||||
<member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0">
|
||||
</member>
|
||||
<member name="spawn_path" type="NodePath" setter="set_spawn_path" getter="get_spawn_path" default="NodePath("")">
|
||||
|
|
|
@ -6,12 +6,64 @@
|
|||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="add_visibility_filter">
|
||||
<return type="void" />
|
||||
<argument index="0" name="filter" type="Callable" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_visibility_for" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<argument index="0" name="peer" type="int" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="remove_visibility_filter">
|
||||
<return type="void" />
|
||||
<argument index="0" name="filter" type="Callable" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_visibility_for">
|
||||
<return type="void" />
|
||||
<argument index="0" name="peer" type="int" />
|
||||
<argument index="1" name="visible" type="bool" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="update_visibility">
|
||||
<return type="void" />
|
||||
<argument index="0" name="for_peer" type="int" default="0" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="public_visibility" type="bool" setter="set_visibility_public" getter="is_visibility_public" default="true">
|
||||
</member>
|
||||
<member name="replication_config" type="SceneReplicationConfig" setter="set_replication_config" getter="get_replication_config">
|
||||
</member>
|
||||
<member name="replication_interval" type="float" setter="set_replication_interval" getter="get_replication_interval" default="0.0">
|
||||
</member>
|
||||
<member name="root_path" type="NodePath" setter="set_root_path" getter="get_root_path" default="NodePath("..")">
|
||||
</member>
|
||||
<member name="visibility_update_mode" type="int" setter="set_visibility_update_mode" getter="get_visibility_update_mode" enum="MultiplayerSynchronizer.VisibilityUpdateMode" default="0">
|
||||
</member>
|
||||
</members>
|
||||
<signals>
|
||||
<signal name="visibility_changed">
|
||||
<argument index="0" name="for_peer" type="int" />
|
||||
<description>
|
||||
</description>
|
||||
</signal>
|
||||
</signals>
|
||||
<constants>
|
||||
<constant name="VISIBILITY_PROCESS_IDLE" value="0" enum="VisibilityUpdateMode">
|
||||
</constant>
|
||||
<constant name="VISIBILITY_PROCESS_PHYSICS" value="1" enum="VisibilityUpdateMode">
|
||||
</constant>
|
||||
<constant name="VISIBILITY_PROCESS_NONE" value="2" enum="VisibilityUpdateMode">
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
||||
|
|
|
@ -71,7 +71,7 @@ bool MultiplayerSpawner::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
}
|
||||
|
||||
void MultiplayerSpawner::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
p_list->push_back(PropertyInfo(Variant::INT, "_spawnable_scene_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Scenes,scenes/"));
|
||||
p_list->push_back(PropertyInfo(Variant::INT, "_spawnable_scene_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Auto Spawn List,scenes/"));
|
||||
List<String> exts;
|
||||
ResourceLoader::get_recognized_extensions_for_type("PackedScene", &exts);
|
||||
String ext_hint;
|
||||
|
@ -144,10 +144,6 @@ void MultiplayerSpawner::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_spawn_limit", "limit"), &MultiplayerSpawner::set_spawn_limit);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "spawn_limit", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_spawn_limit", "get_spawn_limit");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_auto_spawning", "enabled"), &MultiplayerSpawner::set_auto_spawning);
|
||||
ClassDB::bind_method(D_METHOD("is_auto_spawning"), &MultiplayerSpawner::is_auto_spawning);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_spawn"), "set_auto_spawning", "is_auto_spawning");
|
||||
|
||||
GDVIRTUAL_BIND(_spawn_custom, "data");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
|
||||
|
@ -169,7 +165,7 @@ void MultiplayerSpawner::_update_spawn_node() {
|
|||
Node *node = spawn_path.is_empty() && is_inside_tree() ? nullptr : get_node_or_null(spawn_path);
|
||||
if (node) {
|
||||
spawn_node = node->get_instance_id();
|
||||
if (auto_spawn) {
|
||||
if (get_spawnable_scene_count() && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom)) {
|
||||
node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
|
||||
}
|
||||
} else {
|
||||
|
@ -221,15 +217,6 @@ void MultiplayerSpawner::_node_added(Node *p_node) {
|
|||
_track(p_node, Variant(), id);
|
||||
}
|
||||
|
||||
void MultiplayerSpawner::set_auto_spawning(bool p_enabled) {
|
||||
auto_spawn = p_enabled;
|
||||
_update_spawn_node();
|
||||
}
|
||||
|
||||
bool MultiplayerSpawner::is_auto_spawning() const {
|
||||
return auto_spawn;
|
||||
}
|
||||
|
||||
NodePath MultiplayerSpawner::get_spawn_path() const {
|
||||
return spawn_path;
|
||||
}
|
||||
|
|
|
@ -69,7 +69,6 @@ private:
|
|||
|
||||
ObjectID spawn_node;
|
||||
HashMap<ObjectID, SpawnInfo> tracked_nodes;
|
||||
bool auto_spawn = false;
|
||||
uint32_t spawn_limit = 0;
|
||||
|
||||
void _update_spawn_node();
|
||||
|
@ -102,8 +101,6 @@ public:
|
|||
void set_spawn_path(const NodePath &p_path);
|
||||
uint32_t get_spawn_limit() const { return spawn_limit; }
|
||||
void set_spawn_limit(uint32_t p_limit) { spawn_limit = p_limit; }
|
||||
bool is_auto_spawning() const;
|
||||
void set_auto_spawning(bool p_enabled);
|
||||
|
||||
const Variant get_spawn_argument(const ObjectID &p_id) const;
|
||||
int find_spawnable_scene_index_from_object(const ObjectID &p_id) const;
|
||||
|
|
|
@ -43,6 +43,11 @@ Object *MultiplayerSynchronizer::_get_prop_target(Object *p_obj, const NodePath
|
|||
}
|
||||
|
||||
void MultiplayerSynchronizer::_stop() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
|
||||
if (node) {
|
||||
get_multiplayer()->replication_stop(node, this);
|
||||
|
@ -50,9 +55,42 @@ void MultiplayerSynchronizer::_stop() {
|
|||
}
|
||||
|
||||
void MultiplayerSynchronizer::_start() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
|
||||
if (node) {
|
||||
get_multiplayer()->replication_start(node, this);
|
||||
_update_process();
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerSynchronizer::_update_process() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
set_process_internal(false);
|
||||
set_physics_process_internal(false);
|
||||
if (!visibility_filters.size()) {
|
||||
return;
|
||||
}
|
||||
switch (visibility_update_mode) {
|
||||
case VISIBILITY_PROCESS_IDLE:
|
||||
set_process_internal(true);
|
||||
break;
|
||||
case VISIBILITY_PROCESS_PHYSICS:
|
||||
set_physics_process_internal(true);
|
||||
break;
|
||||
case VISIBILITY_PROCESS_NONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,6 +123,66 @@ Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Obj
|
|||
return OK;
|
||||
}
|
||||
|
||||
bool MultiplayerSynchronizer::is_visibility_public() const {
|
||||
return peer_visibility.has(0);
|
||||
}
|
||||
|
||||
void MultiplayerSynchronizer::set_visibility_public(bool p_visible) {
|
||||
set_visibility_for(0, p_visible);
|
||||
}
|
||||
|
||||
bool MultiplayerSynchronizer::is_visible_to(int p_peer) {
|
||||
if (visibility_filters.size()) {
|
||||
Variant arg = p_peer;
|
||||
const Variant *argv[1] = { &arg };
|
||||
for (Callable filter : visibility_filters) {
|
||||
Variant ret;
|
||||
Callable::CallError err;
|
||||
filter.call(argv, 1, ret, err);
|
||||
ERR_FAIL_COND_V(err.error != Callable::CallError::CALL_OK || ret.get_type() != Variant::BOOL, false);
|
||||
if (!ret.operator bool()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return peer_visibility.has(0) || peer_visibility.has(p_peer);
|
||||
}
|
||||
|
||||
void MultiplayerSynchronizer::add_visibility_filter(Callable p_callback) {
|
||||
visibility_filters.insert(p_callback);
|
||||
_update_process();
|
||||
}
|
||||
|
||||
void MultiplayerSynchronizer::remove_visibility_filter(Callable p_callback) {
|
||||
visibility_filters.erase(p_callback);
|
||||
_update_process();
|
||||
}
|
||||
|
||||
void MultiplayerSynchronizer::set_visibility_for(int p_peer, bool p_visible) {
|
||||
if (peer_visibility.has(p_peer) == p_visible) {
|
||||
return;
|
||||
}
|
||||
if (p_visible) {
|
||||
peer_visibility.insert(p_peer);
|
||||
} else {
|
||||
peer_visibility.erase(p_peer);
|
||||
}
|
||||
update_visibility(p_peer);
|
||||
}
|
||||
|
||||
bool MultiplayerSynchronizer::get_visibility_for(int p_peer) const {
|
||||
return peer_visibility.has(p_peer);
|
||||
}
|
||||
|
||||
void MultiplayerSynchronizer::set_visibility_update_mode(VisibilityUpdateMode p_mode) {
|
||||
visibility_update_mode = p_mode;
|
||||
_update_process();
|
||||
}
|
||||
|
||||
MultiplayerSynchronizer::VisibilityUpdateMode MultiplayerSynchronizer::get_visibility_update_mode() const {
|
||||
return visibility_update_mode;
|
||||
}
|
||||
|
||||
void MultiplayerSynchronizer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_root_path", "path"), &MultiplayerSynchronizer::set_root_path);
|
||||
ClassDB::bind_method(D_METHOD("get_root_path"), &MultiplayerSynchronizer::get_root_path);
|
||||
|
@ -95,9 +193,29 @@ void MultiplayerSynchronizer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_replication_config", "config"), &MultiplayerSynchronizer::set_replication_config);
|
||||
ClassDB::bind_method(D_METHOD("get_replication_config"), &MultiplayerSynchronizer::get_replication_config);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_visibility_update_mode", "mode"), &MultiplayerSynchronizer::set_visibility_update_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_visibility_update_mode"), &MultiplayerSynchronizer::get_visibility_update_mode);
|
||||
ClassDB::bind_method(D_METHOD("update_visibility", "for_peer"), &MultiplayerSynchronizer::update_visibility, DEFVAL(0));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_visibility_public", "visible"), &MultiplayerSynchronizer::set_visibility_public);
|
||||
ClassDB::bind_method(D_METHOD("is_visibility_public"), &MultiplayerSynchronizer::is_visibility_public);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_visibility_filter", "filter"), &MultiplayerSynchronizer::add_visibility_filter);
|
||||
ClassDB::bind_method(D_METHOD("remove_visibility_filter", "filter"), &MultiplayerSynchronizer::remove_visibility_filter);
|
||||
ClassDB::bind_method(D_METHOD("set_visibility_for", "peer", "visible"), &MultiplayerSynchronizer::set_visibility_for);
|
||||
ClassDB::bind_method(D_METHOD("get_visibility_for", "peer"), &MultiplayerSynchronizer::get_visibility_for);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "replication_interval", PROPERTY_HINT_RANGE, "0,5,0.001,suffix:s"), "set_replication_interval", "get_replication_interval");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig"), "set_replication_config", "get_replication_config");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig", PROPERTY_USAGE_NO_EDITOR), "set_replication_config", "get_replication_config");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,None"), "set_visibility_update_mode", "get_visibility_update_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "public_visibility"), "set_visibility_public", "is_visibility_public");
|
||||
|
||||
BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_IDLE);
|
||||
BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_PHYSICS);
|
||||
BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_NONE);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("visibility_changed", PropertyInfo(Variant::INT, "for_peer")));
|
||||
}
|
||||
|
||||
void MultiplayerSynchronizer::_notification(int p_what) {
|
||||
|
@ -118,6 +236,11 @@ void MultiplayerSynchronizer::_notification(int p_what) {
|
|||
case NOTIFICATION_EXIT_TREE: {
|
||||
_stop();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PROCESS:
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
update_visibility(0);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,6 +265,18 @@ Ref<SceneReplicationConfig> MultiplayerSynchronizer::get_replication_config() {
|
|||
return replication_config;
|
||||
}
|
||||
|
||||
void MultiplayerSynchronizer::update_visibility(int p_for_peer) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
|
||||
if (node && get_multiplayer()->has_multiplayer_peer() && is_multiplayer_authority()) {
|
||||
emit_signal(SNAME("visibility_changed"), p_for_peer);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerSynchronizer::set_root_path(const NodePath &p_path) {
|
||||
_stop();
|
||||
root_path = p_path;
|
||||
|
@ -162,3 +297,8 @@ void MultiplayerSynchronizer::set_multiplayer_authority(int p_peer_id, bool p_re
|
|||
Node::set_multiplayer_authority(p_peer_id, p_recursive);
|
||||
get_multiplayer()->replication_start(node, this);
|
||||
}
|
||||
|
||||
MultiplayerSynchronizer::MultiplayerSynchronizer() {
|
||||
// Publicly visible by default.
|
||||
peer_visibility.insert(0);
|
||||
}
|
||||
|
|
|
@ -38,14 +38,25 @@
|
|||
class MultiplayerSynchronizer : public Node {
|
||||
GDCLASS(MultiplayerSynchronizer, Node);
|
||||
|
||||
public:
|
||||
enum VisibilityUpdateMode {
|
||||
VISIBILITY_PROCESS_IDLE,
|
||||
VISIBILITY_PROCESS_PHYSICS,
|
||||
VISIBILITY_PROCESS_NONE,
|
||||
};
|
||||
|
||||
private:
|
||||
Ref<SceneReplicationConfig> replication_config;
|
||||
NodePath root_path = NodePath(".."); // Start with parent, like with AnimationPlayer.
|
||||
uint64_t interval_msec = 0;
|
||||
VisibilityUpdateMode visibility_update_mode = VISIBILITY_PROCESS_IDLE;
|
||||
HashSet<Callable> visibility_filters;
|
||||
HashSet<int> peer_visibility;
|
||||
|
||||
static Object *_get_prop_target(Object *p_obj, const NodePath &p_prop);
|
||||
void _start();
|
||||
void _stop();
|
||||
void _update_process();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
@ -66,7 +77,19 @@ public:
|
|||
NodePath get_root_path() const;
|
||||
virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true) override;
|
||||
|
||||
MultiplayerSynchronizer() {}
|
||||
bool is_visibility_public() const;
|
||||
void set_visibility_public(bool p_public);
|
||||
bool is_visible_to(int p_peer);
|
||||
void set_visibility_for(int p_peer, bool p_visible);
|
||||
bool get_visibility_for(int p_peer) const;
|
||||
void update_visibility(int p_for_peer);
|
||||
void set_visibility_update_mode(VisibilityUpdateMode p_mode);
|
||||
void add_visibility_filter(Callable p_callback);
|
||||
void remove_visibility_filter(Callable p_callback);
|
||||
VisibilityUpdateMode get_visibility_update_mode() const;
|
||||
|
||||
MultiplayerSynchronizer();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(MultiplayerSynchronizer::VisibilityUpdateMode);
|
||||
#endif // MULTIPLAYER_SYNCHRONIZER_H
|
||||
|
|
|
@ -187,18 +187,29 @@ bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) {
|
|||
return F->value;
|
||||
}
|
||||
|
||||
bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int p_peer_id, int &r_id) {
|
||||
int SceneCacheInterface::make_object_cache(Object *p_obj) {
|
||||
Node *node = Object::cast_to<Node>(p_obj);
|
||||
ERR_FAIL_COND_V(!node, false);
|
||||
ERR_FAIL_COND_V(!node, -1);
|
||||
NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path());
|
||||
// See if the path is cached.
|
||||
PathSentCache *psc = path_send_cache.getptr(p_path);
|
||||
PathSentCache *psc = path_send_cache.getptr(for_path);
|
||||
if (!psc) {
|
||||
// Path is not cached, create.
|
||||
path_send_cache[p_path] = PathSentCache();
|
||||
psc = path_send_cache.getptr(p_path);
|
||||
path_send_cache[for_path] = PathSentCache();
|
||||
psc = path_send_cache.getptr(for_path);
|
||||
psc->id = last_send_cache_id++;
|
||||
}
|
||||
r_id = psc->id;
|
||||
return psc->id;
|
||||
}
|
||||
|
||||
bool SceneCacheInterface::send_object_cache(Object *p_obj, int p_peer_id, int &r_id) {
|
||||
Node *node = Object::cast_to<Node>(p_obj);
|
||||
ERR_FAIL_COND_V(!node, false);
|
||||
|
||||
r_id = make_object_cache(p_obj);
|
||||
ERR_FAIL_COND_V(r_id < 0, false);
|
||||
NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path());
|
||||
PathSentCache *psc = path_send_cache.getptr(for_path);
|
||||
|
||||
bool has_all_peers = true;
|
||||
List<int> peers_to_add; // If one is missing, take note to add it.
|
||||
|
@ -233,7 +244,7 @@ bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int
|
|||
}
|
||||
|
||||
if (peers_to_add.size()) {
|
||||
_send_confirm_path(node, p_path, psc, peers_to_add);
|
||||
_send_confirm_path(node, for_path, psc, peers_to_add);
|
||||
}
|
||||
|
||||
return has_all_peers;
|
||||
|
|
|
@ -72,7 +72,8 @@ public:
|
|||
virtual void process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) override;
|
||||
|
||||
// Returns true if all peers have cached path.
|
||||
virtual bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id) override;
|
||||
virtual bool send_object_cache(Object *p_obj, int p_target, int &p_id) override;
|
||||
virtual int make_object_cache(Object *p_obj) override;
|
||||
virtual Object *get_cached_object(int p_from, uint32_t p_cache_id) override;
|
||||
virtual bool is_cache_confirmed(NodePath p_path, int p_peer) override;
|
||||
|
||||
|
|
|
@ -60,14 +60,13 @@ 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()) {
|
||||
_send_spawn(rep_state->get_node(oid), rep_state->get_spawner(oid), p_id);
|
||||
_update_spawn_visibility(p_id, oid);
|
||||
}
|
||||
for (const ObjectID &oid : rep_state->get_path_only_nodes()) {
|
||||
Node *node = rep_state->get_node(oid);
|
||||
for (const ObjectID &oid : rep_state->get_synced_nodes()) {
|
||||
MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
|
||||
ERR_CONTINUE(!node || !sync);
|
||||
ERR_CONTINUE(!sync); // ERR_BUG
|
||||
if (sync->is_multiplayer_authority()) {
|
||||
rep_state->peer_add_node(p_id, oid);
|
||||
_update_sync_visibility(p_id, oid);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -97,7 +96,13 @@ Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) {
|
|||
ERR_FAIL_COND_V(!spawner, ERR_INVALID_PARAMETER);
|
||||
Error err = rep_state->config_add_spawn(node, spawner);
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
return _send_spawn(node, spawner, 0);
|
||||
const ObjectID oid = node->get_instance_id();
|
||||
if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
|
||||
rep_state->ensure_net_id(oid);
|
||||
_update_spawn_visibility(0, oid);
|
||||
}
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
|
||||
|
@ -105,9 +110,19 @@ Error SceneReplicationInterface::on_despawn(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(!p_obj || !spawner, ERR_INVALID_PARAMETER);
|
||||
Error err = rep_state->config_del_spawn(node, spawner);
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
return _send_despawn(node, 0);
|
||||
// Forcibly despawn to all peers that knowns me.
|
||||
int len = 0;
|
||||
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)) {
|
||||
continue;
|
||||
}
|
||||
_send_raw(packet_cache.ptr(), len, pid, true);
|
||||
}
|
||||
// Also remove spawner tracking from the replication state.
|
||||
return rep_state->config_del_spawn(node, spawner);
|
||||
}
|
||||
|
||||
Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_config) {
|
||||
|
@ -115,7 +130,15 @@ Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_c
|
|||
ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
|
||||
MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
|
||||
ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
|
||||
|
||||
// Add to synchronizer list and setup visibility.
|
||||
rep_state->config_add_sync(node, sync);
|
||||
const ObjectID oid = node->get_instance_id();
|
||||
sync->connect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed), varray(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.
|
||||
|
@ -127,9 +150,6 @@ Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_c
|
|||
ERR_FAIL_COND_V(err, err);
|
||||
err = MultiplayerSynchronizer::set_state(props, node, vars);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
} else if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) {
|
||||
// Either it's a spawn or a static sync, in any case add it to the list of known nodes.
|
||||
rep_state->peer_add_node(0, p_obj->get_instance_id());
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
@ -138,10 +158,103 @@ Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_co
|
|||
Node *node = Object::cast_to<Node>(p_obj);
|
||||
ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
|
||||
MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
|
||||
ERR_FAIL_COND_V(!p_obj || !sync, ERR_INVALID_PARAMETER);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (p_peer == 0) {
|
||||
for (int pid : rep_state->get_peers()) {
|
||||
// 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) {
|
||||
continue;
|
||||
}
|
||||
if (is_visible) {
|
||||
rep_state->peer_add_sync(pid, p_oid);
|
||||
} else {
|
||||
rep_state->peer_del_sync(pid, p_oid);
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
} else {
|
||||
if (is_visible == rep_state->is_peer_sync(p_peer, p_oid)) {
|
||||
return OK;
|
||||
}
|
||||
if (is_visible) {
|
||||
return rep_state->peer_add_sync(p_peer, p_oid);
|
||||
} else {
|
||||
return rep_state->peer_del_sync(p_peer, p_oid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
ERR_FAIL_COND_V(!node || !spawner || !spawner->is_multiplayer_authority(), ERR_BUG);
|
||||
bool is_visible = !sync || sync->is_visible_to(p_peer);
|
||||
// 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)) {
|
||||
return OK;
|
||||
}
|
||||
if (is_visible) {
|
||||
to_spawn.insert(p_peer);
|
||||
} else {
|
||||
to_despawn.insert(p_peer);
|
||||
}
|
||||
} 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);
|
||||
} else {
|
||||
to_despawn.insert(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (to_spawn.size()) {
|
||||
int len = 0;
|
||||
_make_spawn_packet(node, len);
|
||||
for (int pid : to_spawn) {
|
||||
int path_id;
|
||||
multiplayer->send_object_cache(spawner, pid, path_id);
|
||||
_send_raw(packet_cache.ptr(), len, pid, true);
|
||||
rep_state->peer_add_spawn(pid, 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);
|
||||
_send_raw(packet_cache.ptr(), len, pid, true);
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable) {
|
||||
ERR_FAIL_COND_V(!p_buffer || p_size < 1, ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V(!multiplayer, ERR_UNCONFIGURED);
|
||||
|
@ -158,18 +271,20 @@ Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size,
|
|||
return peer->put_packet(p_buffer, p_size);
|
||||
}
|
||||
|
||||
Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p_spawner, int p_peer) {
|
||||
ERR_FAIL_COND_V(p_peer < 0, ERR_BUG);
|
||||
Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, int &r_len) {
|
||||
ERR_FAIL_COND_V(!multiplayer, ERR_BUG);
|
||||
ERR_FAIL_COND_V(!p_spawner || !p_node, ERR_BUG);
|
||||
|
||||
const ObjectID oid = p_node->get_instance_id();
|
||||
uint32_t nid = rep_state->ensure_net_id(oid);
|
||||
MultiplayerSpawner *spawner = rep_state->get_spawner(oid);
|
||||
ERR_FAIL_COND_V(!spawner || !p_node, ERR_BUG);
|
||||
|
||||
uint32_t nid = rep_state->get_net_id(oid);
|
||||
ERR_FAIL_COND_V(!nid, ERR_UNCONFIGURED);
|
||||
|
||||
// Prepare custom arg and scene_id
|
||||
uint8_t scene_id = p_spawner->find_spawnable_scene_index_from_object(oid);
|
||||
uint8_t scene_id = spawner->find_spawnable_scene_index_from_object(oid);
|
||||
bool is_custom = scene_id == MultiplayerSpawner::INVALID_ID;
|
||||
Variant spawn_arg = p_spawner->get_spawn_argument(oid);
|
||||
Variant spawn_arg = 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);
|
||||
|
@ -181,7 +296,8 @@ Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p
|
|||
Vector<Variant> state_vars;
|
||||
Vector<const Variant *> state_varp;
|
||||
MultiplayerSynchronizer *synchronizer = rep_state->get_synchronizer(oid);
|
||||
if (synchronizer && synchronizer->get_replication_config().is_valid()) {
|
||||
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);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to retrieve spawn state.");
|
||||
|
@ -189,13 +305,8 @@ Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p
|
|||
ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to encode spawn state.");
|
||||
}
|
||||
|
||||
// Prepare simplified path.
|
||||
NodePath rel_path = multiplayer->get_root_path().rel_path_to(p_spawner->get_path());
|
||||
|
||||
int path_id = 0;
|
||||
multiplayer->send_object_cache(p_spawner, rel_path, p_peer, path_id);
|
||||
|
||||
// Encode name and parent ID.
|
||||
// Encode scene ID, path ID, net ID, node name.
|
||||
int path_id = multiplayer->make_object_cache(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);
|
||||
|
@ -220,12 +331,11 @@ Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p
|
|||
ERR_FAIL_COND_V(err, err);
|
||||
ofs += state_size;
|
||||
}
|
||||
Error err = _send_raw(ptr, ofs, p_peer, true);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
return rep_state->peer_add_node(p_peer, oid);
|
||||
r_len = ofs;
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error SceneReplicationInterface::_send_despawn(Node *p_node, int p_peer) {
|
||||
Error SceneReplicationInterface::_make_despawn_packet(Node *p_node, int &r_len) {
|
||||
const ObjectID oid = p_node->get_instance_id();
|
||||
MAKE_ROOM(5);
|
||||
uint8_t *ptr = packet_cache.ptrw();
|
||||
|
@ -233,9 +343,8 @@ Error SceneReplicationInterface::_send_despawn(Node *p_node, int p_peer) {
|
|||
int ofs = 1;
|
||||
uint32_t nid = rep_state->get_net_id(oid);
|
||||
ofs += encode_uint32(nid, &ptr[ofs]);
|
||||
Error err = _send_raw(ptr, ofs, p_peer, true);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
return rep_state->peer_del_node(p_peer, oid);
|
||||
r_len = ofs;
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
|
||||
|
@ -316,8 +425,8 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p
|
|||
}
|
||||
|
||||
void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
|
||||
const HashSet<ObjectID> &known = rep_state->get_known_nodes(p_peer);
|
||||
if (known.is_empty()) {
|
||||
const HashSet<ObjectID> &to_sync = rep_state->get_peer_sync_nodes(p_peer);
|
||||
if (to_sync.is_empty()) {
|
||||
return;
|
||||
}
|
||||
MAKE_ROOM(sync_mtu);
|
||||
|
@ -327,14 +436,29 @@ void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
|
|||
ofs += encode_uint16(rep_state->peer_sync_next(p_peer), &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 : known) {
|
||||
for (const ObjectID &oid : to_sync) {
|
||||
if (!rep_state->update_sync_time(oid, p_msec)) {
|
||||
continue; // nothing to sync.
|
||||
}
|
||||
MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
|
||||
ERR_CONTINUE(!sync);
|
||||
ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid());
|
||||
Node *node = rep_state->get_node(oid);
|
||||
ERR_CONTINUE(!node);
|
||||
uint32_t net_id = rep_state->get_net_id(oid);
|
||||
if (net_id == 0 || (net_id & 0x80000000)) {
|
||||
int path_id = 0;
|
||||
bool verified = multiplayer->send_object_cache(sync, p_peer, path_id);
|
||||
ERR_CONTINUE_MSG(path_id < 0, "This should never happen!");
|
||||
if (net_id == 0) {
|
||||
// First time path based ID.
|
||||
net_id = path_id | 0x80000000;
|
||||
rep_state->set_net_id(oid, net_id | 0x80000000);
|
||||
}
|
||||
if (!verified) {
|
||||
// The path based sync is not yet confirmed, skipping.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
int size;
|
||||
Vector<Variant> vars;
|
||||
Vector<const Variant *> varp;
|
||||
|
@ -351,16 +475,6 @@ void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
|
|||
ofs = 3;
|
||||
}
|
||||
if (size) {
|
||||
uint32_t net_id = rep_state->get_net_id(oid);
|
||||
if (net_id == 0 || (net_id & 0x80000000)) {
|
||||
// First time path based ID.
|
||||
NodePath rel_path = multiplayer->get_root_path().rel_path_to(sync->get_path());
|
||||
int path_id = 0;
|
||||
multiplayer->send_object_cache(sync, rel_path, p_peer, path_id);
|
||||
ERR_CONTINUE_MSG(net_id && net_id != (uint32_t(path_id) | 0x80000000), "This should never happen!");
|
||||
net_id = path_id;
|
||||
rep_state->set_net_id(oid, net_id | 0x80000000);
|
||||
}
|
||||
ofs += encode_uint32(rep_state->get_net_id(oid), &ptr[ofs]);
|
||||
ofs += encode_uint32(size, &ptr[ofs]);
|
||||
MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), &ptr[ofs], size);
|
||||
|
|
|
@ -40,10 +40,13 @@ class SceneReplicationInterface : public MultiplayerReplicationInterface {
|
|||
|
||||
private:
|
||||
void _send_sync(int p_peer, uint64_t p_msec);
|
||||
Error _send_spawn(Node *p_node, MultiplayerSpawner *p_spawner, int p_peer);
|
||||
Error _send_despawn(Node *p_node, int p_peer);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
Ref<SceneReplicationState> rep_state;
|
||||
|
|
|
@ -56,7 +56,8 @@ void SceneReplicationState::_untrack(const ObjectID &p_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.known_nodes.erase(p_id);
|
||||
E.value.sync_nodes.erase(p_id);
|
||||
E.value.spawn_nodes.erase(p_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,11 +94,6 @@ bool SceneReplicationState::update_sync_time(const ObjectID &p_id, uint64_t p_ms
|
|||
return false;
|
||||
}
|
||||
|
||||
const HashSet<ObjectID> SceneReplicationState::get_known_nodes(int p_peer) {
|
||||
ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
|
||||
return peers_info[p_peer].known_nodes;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -147,8 +143,6 @@ Error SceneReplicationState::config_add_spawn(Node *p_node, MultiplayerSpawner *
|
|||
ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE);
|
||||
tobj.spawner = p_spawner->get_instance_id();
|
||||
spawned_nodes.insert(oid);
|
||||
// The spawner may be notified after the synchronizer.
|
||||
path_only_nodes.erase(oid);
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -159,6 +153,9 @@ Error SceneReplicationState::config_del_spawn(Node *p_node, MultiplayerSpawner *
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -167,10 +164,7 @@ Error SceneReplicationState::config_add_sync(Node *p_node, MultiplayerSynchroniz
|
|||
TrackedNode &tobj = _track(oid);
|
||||
ERR_FAIL_COND_V(tobj.synchronizer != ObjectID(), ERR_ALREADY_IN_USE);
|
||||
tobj.synchronizer = p_sync->get_instance_id();
|
||||
// If it doesn't have a spawner, we might need to assign ID for this node using it's path.
|
||||
if (tobj.spawner.is_null()) {
|
||||
path_only_nodes.insert(oid);
|
||||
}
|
||||
synced_nodes.insert(oid);
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -180,38 +174,57 @@ Error SceneReplicationState::config_del_sync(Node *p_node, MultiplayerSynchroniz
|
|||
TrackedNode &tobj = _track(oid);
|
||||
ERR_FAIL_COND_V(tobj.synchronizer != p_sync->get_instance_id(), ERR_INVALID_PARAMETER);
|
||||
tobj.synchronizer = ObjectID();
|
||||
if (path_only_nodes.has(oid)) {
|
||||
p_node->disconnect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack));
|
||||
_untrack(oid);
|
||||
path_only_nodes.erase(oid);
|
||||
synced_nodes.erase(oid);
|
||||
for (KeyValue<int, PeerInfo> &E : peers_info) {
|
||||
E.value.sync_nodes.erase(oid);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error SceneReplicationState::peer_add_node(int p_peer, const ObjectID &p_id) {
|
||||
if (p_peer) {
|
||||
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].known_nodes.insert(p_id);
|
||||
} else {
|
||||
for (KeyValue<int, PeerInfo> &E : peers_info) {
|
||||
E.value.known_nodes.insert(p_id);
|
||||
}
|
||||
}
|
||||
peers_info[p_peer].sync_nodes.insert(p_id);
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error SceneReplicationState::peer_del_node(int p_peer, const ObjectID &p_id) {
|
||||
if (p_peer) {
|
||||
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].known_nodes.erase(p_id);
|
||||
} else {
|
||||
for (KeyValue<int, PeerInfo> &E : peers_info) {
|
||||
E.value.known_nodes.erase(p_id);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -62,7 +62,8 @@ private:
|
|||
};
|
||||
|
||||
struct PeerInfo {
|
||||
HashSet<ObjectID> known_nodes;
|
||||
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;
|
||||
|
@ -73,7 +74,7 @@ private:
|
|||
HashMap<ObjectID, TrackedNode> tracked_nodes;
|
||||
HashMap<int, PeerInfo> peers_info;
|
||||
HashSet<ObjectID> spawned_nodes;
|
||||
HashSet<ObjectID> path_only_nodes;
|
||||
HashSet<ObjectID> synced_nodes;
|
||||
|
||||
TrackedNode &_track(const ObjectID &p_id);
|
||||
void _untrack(const ObjectID &p_id);
|
||||
|
@ -82,7 +83,9 @@ private:
|
|||
public:
|
||||
const HashSet<int> get_peers() const { return known_peers; }
|
||||
const HashSet<ObjectID> &get_spawned_nodes() const { return spawned_nodes; }
|
||||
const HashSet<ObjectID> &get_path_only_nodes() const { return path_only_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; }
|
||||
|
@ -90,7 +93,6 @@ public:
|
|||
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);
|
||||
|
||||
const HashSet<ObjectID> get_known_nodes(int p_peer);
|
||||
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);
|
||||
|
@ -104,8 +106,17 @@ public:
|
|||
Error config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
|
||||
Error config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
|
||||
|
||||
Error peer_add_node(int p_peer, const ObjectID &p_id);
|
||||
Error peer_del_node(int p_peer, const ObjectID &p_id);
|
||||
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);
|
||||
|
|
|
@ -302,12 +302,9 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
|
|||
ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + ".");
|
||||
}
|
||||
|
||||
NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path());
|
||||
ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!");
|
||||
|
||||
// See if all peers have cached path (if so, call can be fast).
|
||||
int psc_id;
|
||||
const bool has_all_peers = multiplayer->send_object_cache(p_from, from_path, p_to, psc_id);
|
||||
const bool has_all_peers = multiplayer->send_object_cache(p_from, p_to, psc_id);
|
||||
|
||||
// Create base packet, lots of hardcode because it must be tight.
|
||||
|
||||
|
@ -414,6 +411,7 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
|
|||
// Not all verified path, so send one by one.
|
||||
|
||||
// Append path at the end, since we will need it for some packets.
|
||||
NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path());
|
||||
CharString pname = String(from_path).utf8();
|
||||
int path_len = encode_cstring(pname.get_data(), nullptr);
|
||||
MAKE_ROOM(ofs + path_len);
|
||||
|
|
Loading…
Reference in a new issue