Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add peer visibility to MultiplayerSynchronizer. #62961

Merged
merged 1 commit into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions core/multiplayer/multiplayer_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 4 additions & 2 deletions core/multiplayer/multiplayer_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ class MultiplayerCacheInterface : public RefCounted {
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; }

Expand Down Expand Up @@ -160,7 +161,8 @@ class MultiplayerAPI : public RefCounted {
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);

Expand Down
2 changes: 0 additions & 2 deletions doc/classes/MultiplayerSpawner.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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(&quot;&quot;)">
Expand Down
52 changes: 52 additions & 0 deletions doc/classes/MultiplayerSynchronizer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Faless marked this conversation as resolved.
Show resolved Hide resolved
</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(&quot;..&quot;)">
</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>
17 changes: 2 additions & 15 deletions scene/multiplayer/multiplayer_spawner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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/"));
Faless marked this conversation as resolved.
Show resolved Hide resolved
List<String> exts;
ResourceLoader::get_recognized_extensions_for_type("PackedScene", &exts);
String ext_hint;
Expand Down Expand Up @@ -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")));
Expand All @@ -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 {
Expand Down Expand Up @@ -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;
}
Expand Down
3 changes: 0 additions & 3 deletions scene/multiplayer/multiplayer_spawner.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ class MultiplayerSpawner : public Node {

ObjectID spawn_node;
HashMap<ObjectID, SpawnInfo> tracked_nodes;
bool auto_spawn = false;
uint32_t spawn_limit = 0;

void _update_spawn_node();
Expand Down Expand Up @@ -102,8 +101,6 @@ class MultiplayerSpawner : public Node {
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;
Expand Down
142 changes: 141 additions & 1 deletion scene/multiplayer/multiplayer_synchronizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,54 @@ 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);
}
}

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

Expand Down Expand Up @@ -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);
Expand All @@ -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) {
Expand All @@ -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;
}
}

Expand All @@ -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;
Expand All @@ -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);
}
Loading