summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/MultiplayerAPI.xml4
-rw-r--r--doc/classes/Node.xml5
-rw-r--r--doc/classes/SceneTree.xml18
-rw-r--r--scene/main/node.cpp16
-rw-r--r--scene/main/node.h2
-rw-r--r--scene/main/scene_tree.cpp63
-rw-r--r--scene/main/scene_tree.h5
7 files changed, 73 insertions, 40 deletions
diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml
index ac17886183..059d147979 100644
--- a/doc/classes/MultiplayerAPI.xml
+++ b/doc/classes/MultiplayerAPI.xml
@@ -6,7 +6,7 @@
<description>
This class implements the high-level multiplayer API. See also [MultiplayerPeer].
By default, [SceneTree] has a reference to this class that is used to provide multiplayer capabilities (i.e. RPCs) across the whole scene.
- It is possible to override the MultiplayerAPI instance used by specific Nodes by setting the [member Node.custom_multiplayer] property, effectively allowing to run both client and server in the same scene.
+ It is possible to override the MultiplayerAPI instance used by specific tree branches by calling the [method SceneTree.set_multiplayer] method, effectively allowing to run both client and server in the same scene.
[b]Note:[/b] The high-level multiplayer API protocol is an implementation detail and isn't meant to be used by non-Godot servers. It may change without notice.
[b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.
</description>
@@ -53,7 +53,7 @@
<method name="poll">
<return type="void" />
<description>
- Method used for polling the MultiplayerAPI. You only need to worry about this if you are using [member Node.custom_multiplayer] override or you set [member SceneTree.multiplayer_poll] to [code]false[/code]. By default, [SceneTree] will poll its MultiplayerAPI for you.
+ Method used for polling the MultiplayerAPI. You only need to worry about this if you set [member SceneTree.multiplayer_poll] to [code]false[/code]. By default, [SceneTree] will poll its MultiplayerAPI(s) for you.
[b]Note:[/b] This method results in RPCs being called, so they will be executed in the same context of this function (e.g. [code]_process[/code], [code]physics[/code], [Thread]).
</description>
</method>
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 56c80078b3..c9795856d5 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -745,14 +745,11 @@
</method>
</methods>
<members>
- <member name="custom_multiplayer" type="MultiplayerAPI" setter="set_custom_multiplayer" getter="get_custom_multiplayer">
- The override to the default [MultiplayerAPI]. Set to [code]null[/code] to use the default [SceneTree] one.
- </member>
<member name="editor_description" type="String" setter="set_editor_description" getter="get_editor_description" default="&quot;&quot;">
Add a custom description to a node. It will be displayed in a tooltip when hovered in editor's scene tree.
</member>
<member name="multiplayer" type="MultiplayerAPI" setter="" getter="get_multiplayer">
- The [MultiplayerAPI] instance associated with this node. Either the [member custom_multiplayer], or the default SceneTree one (if inside tree).
+ The [MultiplayerAPI] instance associated with this node. See [method SceneTree.get_multiplayer].
</member>
<member name="name" type="StringName" setter="set_name" getter="get_name">
The name of the node. This name is unique among the siblings (other child nodes from the same parent). When set to an existing name, the node will be automatically renamed.
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index 77023d2126..ddff766b17 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -99,6 +99,13 @@
Returns the current frame number, i.e. the total frame count since the application started.
</description>
</method>
+ <method name="get_multiplayer" qualifiers="const">
+ <return type="MultiplayerAPI" />
+ <argument index="0" name="for_path" type="NodePath" default="NodePath(&quot;&quot;)" />
+ <description>
+ Return the [MultiplayerAPI] configured for the given path, or the default one if [code]for_path[/code] is empty.
+ </description>
+ </method>
<method name="get_node_count" qualifiers="const">
<return type="int" />
<description>
@@ -193,6 +200,14 @@
Sets the given [code]property[/code] to [code]value[/code] on all members of the given group, respecting the given [enum GroupCallFlags].
</description>
</method>
+ <method name="set_multiplayer">
+ <return type="void" />
+ <argument index="0" name="multiplayer" type="MultiplayerAPI" />
+ <argument index="1" name="root_path" type="NodePath" default="NodePath(&quot;&quot;)" />
+ <description>
+ Sets a custom [MultiplayerAPI] with the given [code]root_path[/code] (controlling also the relative subpaths), or override the default one if [code]root_path[/code] is empty.
+ </description>
+ </method>
<method name="set_quit_on_go_back">
<return type="void" />
<argument index="0" name="enabled" type="bool" />
@@ -215,9 +230,6 @@
<member name="edited_scene_root" type="Node" setter="set_edited_scene_root" getter="get_edited_scene_root">
The root of the edited scene.
</member>
- <member name="multiplayer" type="MultiplayerAPI" setter="set_multiplayer" getter="get_multiplayer">
- The default [MultiplayerAPI] instance for this [SceneTree].
- </member>
<member name="multiplayer_poll" type="bool" setter="set_multiplayer_poll_enabled" getter="is_multiplayer_poll_enabled" default="true">
If [code]true[/code] (default value), enables automatic polling of the [MultiplayerAPI] for this SceneTree during [signal process_frame].
If [code]false[/code], you need to manually call [method MultiplayerAPI.poll] to process network packets and deliver RPCs. This allows running RPCs in a different loop (e.g. physics, thread, specific time step) and for manual [Mutex] protection when accessing the [MultiplayerAPI] from threads.
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 25e266c38e..f1c0260dd5 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -653,21 +653,10 @@ void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg
}
Ref<MultiplayerAPI> Node::get_multiplayer() const {
- if (multiplayer.is_valid()) {
- return multiplayer;
- }
if (!is_inside_tree()) {
return Ref<MultiplayerAPI>();
}
- return get_tree()->get_multiplayer();
-}
-
-Ref<MultiplayerAPI> Node::get_custom_multiplayer() const {
- return multiplayer;
-}
-
-void Node::set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
- multiplayer = p_multiplayer;
+ return get_tree()->get_multiplayer(get_path());
}
Vector<Multiplayer::RPCConfig> Node::get_node_rpc_methods() const {
@@ -2892,8 +2881,6 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_multiplayer_authority"), &Node::is_multiplayer_authority);
ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer);
- ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer);
- ClassDB::bind_method(D_METHOD("set_custom_multiplayer", "api"), &Node::set_custom_multiplayer);
ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "call_local", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(false), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description);
@@ -2999,7 +2986,6 @@ void Node::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_file_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_scene_file_path", "get_scene_file_path");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_owner", "get_owner");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "", "get_multiplayer");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "set_custom_multiplayer", "get_custom_multiplayer");
ADD_GROUP("Process", "process_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Inherit,Pausable,When Paused,Always,Disabled"), "set_process_mode", "get_process_mode");
diff --git a/scene/main/node.h b/scene/main/node.h
index 923d03dcfe..fb84aabb62 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -515,8 +515,6 @@ public:
void rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
Ref<MultiplayerAPI> get_multiplayer() const;
- Ref<MultiplayerAPI> get_custom_multiplayer() const;
- void set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
Node();
~Node();
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 151239c9e7..d42c2aadad 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -438,6 +438,10 @@ bool SceneTree::process(double p_time) {
if (multiplayer_poll) {
multiplayer->poll();
+ const NodePath *rpath = nullptr;
+ while ((rpath = custom_multiplayers.next(rpath))) {
+ custom_multiplayers[*rpath]->poll();
+ }
}
emit_signal(SNAME("process_frame"));
@@ -1133,8 +1137,51 @@ Array SceneTree::get_processed_tweens() {
return ret;
}
-Ref<MultiplayerAPI> SceneTree::get_multiplayer() const {
- return multiplayer;
+Ref<MultiplayerAPI> SceneTree::get_multiplayer(const NodePath &p_for_path) const {
+ Ref<MultiplayerAPI> out = multiplayer;
+ const NodePath *spath = nullptr;
+ while ((spath = custom_multiplayers.next(spath))) {
+ const Vector<StringName> snames = (*spath).get_names();
+ const Vector<StringName> tnames = p_for_path.get_names();
+ if (tnames.size() < snames.size()) {
+ continue;
+ }
+ const StringName *sptr = snames.ptr();
+ const StringName *nptr = tnames.ptr();
+ bool valid = true;
+ for (int i = 0; i < snames.size(); i++) {
+ if (sptr[i] != nptr[i]) {
+ valid = false;
+ break;
+ }
+ }
+ if (valid) {
+ out = custom_multiplayers[*spath];
+ break;
+ }
+ }
+ return out;
+}
+
+void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePath &p_root_path) {
+ if (p_root_path.is_empty()) {
+ ERR_FAIL_COND(!p_multiplayer.is_valid());
+ if (multiplayer.is_valid()) {
+ multiplayer->set_root_path(NodePath());
+ }
+ multiplayer = p_multiplayer;
+ multiplayer->set_root_path("/" + root->get_name());
+ } else {
+ if (p_multiplayer.is_valid()) {
+ custom_multiplayers[p_root_path] = p_multiplayer;
+ p_multiplayer->set_root_path(p_root_path);
+ } else {
+ if (custom_multiplayers.has(p_root_path)) {
+ custom_multiplayers[p_root_path]->set_root_path(NodePath());
+ custom_multiplayers.erase(p_root_path);
+ }
+ }
+ }
}
void SceneTree::set_multiplayer_poll_enabled(bool p_enabled) {
@@ -1145,13 +1192,6 @@ bool SceneTree::is_multiplayer_poll_enabled() const {
return multiplayer_poll;
}
-void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
- ERR_FAIL_COND(!p_multiplayer.is_valid());
-
- multiplayer = p_multiplayer;
- multiplayer->set_root_path("/" + root->get_name());
-}
-
void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_root"), &SceneTree::get_root);
ClassDB::bind_method(D_METHOD("has_group", "name"), &SceneTree::has_group);
@@ -1214,8 +1254,8 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene);
- ClassDB::bind_method(D_METHOD("set_multiplayer", "multiplayer"), &SceneTree::set_multiplayer);
- ClassDB::bind_method(D_METHOD("get_multiplayer"), &SceneTree::get_multiplayer);
+ ClassDB::bind_method(D_METHOD("set_multiplayer", "multiplayer", "root_path"), &SceneTree::set_multiplayer, DEFVAL(NodePath()));
+ ClassDB::bind_method(D_METHOD("get_multiplayer", "for_path"), &SceneTree::get_multiplayer, DEFVAL(NodePath()));
ClassDB::bind_method(D_METHOD("set_multiplayer_poll_enabled", "enabled"), &SceneTree::set_multiplayer_poll_enabled);
ClassDB::bind_method(D_METHOD("is_multiplayer_poll_enabled"), &SceneTree::is_multiplayer_poll_enabled);
@@ -1225,7 +1265,6 @@ void SceneTree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "edited_scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_edited_scene_root", "get_edited_scene_root");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "current_scene", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_current_scene", "get_current_scene");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "", "get_root");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "set_multiplayer", "get_multiplayer");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multiplayer_poll"), "set_multiplayer_poll_enabled", "is_multiplayer_poll_enabled");
ADD_SIGNAL(MethodInfo("tree_changed"));
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 705ca6ebd3..9d7757e0a3 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -159,6 +159,7 @@ private:
///network///
Ref<MultiplayerAPI> multiplayer;
+ HashMap<NodePath, Ref<MultiplayerAPI>> custom_multiplayers;
bool multiplayer_poll = true;
static SceneTree *singleton;
@@ -351,10 +352,10 @@ public:
//network API
- Ref<MultiplayerAPI> get_multiplayer() const;
+ Ref<MultiplayerAPI> get_multiplayer(const NodePath &p_for_path = NodePath()) const;
+ void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePath &p_root_path = NodePath());
void set_multiplayer_poll_enabled(bool p_enabled);
bool is_multiplayer_poll_enabled() const;
- void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
static void add_idle_callback(IdleCallback p_callback);