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

Disallow nested custom multiplayers in SceneTree #77829

Merged
merged 1 commit into from
Sep 26, 2023
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
1 change: 1 addition & 0 deletions doc/classes/Node.xml
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,7 @@
</member>
<member name="multiplayer" type="MultiplayerAPI" setter="" getter="get_multiplayer">
The [MultiplayerAPI] instance associated with this node. See [method SceneTree.get_multiplayer].
[b]Note:[/b] Renaming the node, or moving it in the tree, will not move the [MultiplayerAPI] to the new path, you will have to update this manually.
</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.
Expand Down
5 changes: 2 additions & 3 deletions doc/classes/SceneTree.xml
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@
<return type="MultiplayerAPI" />
<param 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 [param for_path] is empty.
[b]Note:[/b] Only one [MultiplayerAPI] may be configured for any subpath. If one is configured for [code]"/root/Foo"[/code] then calling this for [code]"/root/Foo/Bar"[/code] will return the one configured for [code]"/root/Foo"[/code], regardless if one is configured for that path.
Searches for the [MultiplayerAPI] configured for the given path, if one does not exist it searches the parent paths until one is found. If the path is empty, or none is found, the default one is returned. See [method set_multiplayer].
</description>
</method>
<method name="get_node_count" qualifiers="const">
Expand Down Expand Up @@ -211,7 +210,7 @@
<param index="1" name="root_path" type="NodePath" default="NodePath(&quot;&quot;)" />
<description>
Sets a custom [MultiplayerAPI] with the given [param root_path] (controlling also the relative subpaths), or override the default one if [param root_path] is empty.
[b]Note:[/b] Only one [MultiplayerAPI] may be configured for any subpath. If one is configured for [code]"/root/Foo"[/code] setting one for [code]"/root/Foo/Bar"[/code] will be ignored. See [method get_multiplayer].
[b]Note:[/b] No [MultiplayerAPI] must be configured for the subpath containing [param root_path], nested custom multiplayers are not allowed. I.e. if one is configured for [code]"/root/Foo"[/code] setting one for [code]"/root/Foo/Bar"[/code] will cause an error.
</description>
</method>
<method name="unload_current_scene">
Expand Down
35 changes: 29 additions & 6 deletions scene/main/scene_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1483,15 +1483,18 @@ TypedArray<Tween> SceneTree::get_processed_tweens() {

Ref<MultiplayerAPI> SceneTree::get_multiplayer(const NodePath &p_for_path) const {
ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), Ref<MultiplayerAPI>(), "Multiplayer can only be manipulated from the main thread.");
Ref<MultiplayerAPI> out = multiplayer;
if (p_for_path.is_empty()) {
return multiplayer;
}

const Vector<StringName> tnames = p_for_path.get_names();
const StringName *nptr = tnames.ptr();
for (const KeyValue<NodePath, Ref<MultiplayerAPI>> &E : custom_multiplayers) {
const Vector<StringName> snames = E.key.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]) {
Expand All @@ -1500,11 +1503,11 @@ Ref<MultiplayerAPI> SceneTree::get_multiplayer(const NodePath &p_for_path) const
}
}
if (valid) {
out = E.value;
break;
return E.value;
}
}
return out;

return multiplayer;
}

void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePath &p_root_path) {
Expand All @@ -1519,10 +1522,30 @@ void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePat
} else {
if (custom_multiplayers.has(p_root_path)) {
custom_multiplayers[p_root_path]->object_configuration_remove(nullptr, p_root_path);
} else if (p_multiplayer.is_valid()) {
const Vector<StringName> tnames = p_root_path.get_names();
const StringName *nptr = tnames.ptr();
for (const KeyValue<NodePath, Ref<MultiplayerAPI>> &E : custom_multiplayers) {
const Vector<StringName> snames = E.key.get_names();
if (tnames.size() < snames.size()) {
continue;
}
const StringName *sptr = snames.ptr();
bool valid = true;
for (int i = 0; i < snames.size(); i++) {
if (sptr[i] != nptr[i]) {
valid = false;
break;
}
}
ERR_FAIL_COND_MSG(valid, "Multiplayer is already configured for a parent of this path: '" + p_root_path + "' in '" + E.key + "'.");
}
Faless marked this conversation as resolved.
Show resolved Hide resolved
}
if (p_multiplayer.is_valid()) {
custom_multiplayers[p_root_path] = p_multiplayer;
p_multiplayer->object_configuration_add(nullptr, p_root_path);
} else {
custom_multiplayers.erase(p_root_path);
}
}
}
Expand Down