summaryrefslogtreecommitdiffstats
path: root/modules/multiplayer
diff options
context:
space:
mode:
Diffstat (limited to 'modules/multiplayer')
-rw-r--r--modules/multiplayer/doc_classes/SceneReplicationConfig.xml38
-rw-r--r--modules/multiplayer/editor/editor_network_profiler.cpp23
-rw-r--r--modules/multiplayer/editor/replication_editor.cpp141
-rw-r--r--modules/multiplayer/editor/replication_editor.h6
-rw-r--r--modules/multiplayer/multiplayer_debugger.cpp4
-rw-r--r--modules/multiplayer/multiplayer_spawner.cpp6
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.cpp10
-rw-r--r--modules/multiplayer/scene_cache_interface.cpp14
-rw-r--r--modules/multiplayer/scene_cache_interface.h1
-rw-r--r--modules/multiplayer/scene_replication_config.cpp167
-rw-r--r--modules/multiplayer/scene_replication_config.h24
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp28
-rw-r--r--modules/multiplayer/scene_rpc_interface.cpp6
13 files changed, 276 insertions, 192 deletions
diff --git a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
index b976eea30b..1a51e4b6e9 100644
--- a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
+++ b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
@@ -37,6 +37,13 @@
Finds the index of the given [param path].
</description>
</method>
+ <method name="property_get_replication_mode">
+ <return type="int" enum="SceneReplicationConfig.ReplicationMode" />
+ <param index="0" name="path" type="NodePath" />
+ <description>
+ Returns the replication mode for the property identified by the given [param path]. See [enum ReplicationMode].
+ </description>
+ </method>
<method name="property_get_spawn">
<return type="bool" />
<param index="0" name="path" type="NodePath" />
@@ -44,18 +51,28 @@
Returns whether the property identified by the given [param path] is configured to be synchronized on spawn.
</description>
</method>
- <method name="property_get_sync">
+ <method name="property_get_sync" is_deprecated="true">
<return type="bool" />
<param index="0" name="path" type="NodePath" />
<description>
Returns whether the property identified by the given [param path] is configured to be synchronized on process.
+ [i]Deprecated.[/i] Use [method property_get_replication_mode] instead.
</description>
</method>
- <method name="property_get_watch">
+ <method name="property_get_watch" is_deprecated="true">
<return type="bool" />
<param index="0" name="path" type="NodePath" />
<description>
Returns whether the property identified by the given [param path] is configured to be reliably synchronized when changes are detected on process.
+ [i]Deprecated.[/i] Use [method property_get_replication_mode] instead.
+ </description>
+ </method>
+ <method name="property_set_replication_mode">
+ <return type="void" />
+ <param index="0" name="path" type="NodePath" />
+ <param index="1" name="mode" type="int" enum="SceneReplicationConfig.ReplicationMode" />
+ <description>
+ Sets the synchronization mode for the property identified by the given [param path]. See [enum ReplicationMode].
</description>
</method>
<method name="property_set_spawn">
@@ -66,20 +83,22 @@
Sets whether the property identified by the given [param path] is configured to be synchronized on spawn.
</description>
</method>
- <method name="property_set_sync">
+ <method name="property_set_sync" is_deprecated="true">
<return type="void" />
<param index="0" name="path" type="NodePath" />
<param index="1" name="enabled" type="bool" />
<description>
Sets whether the property identified by the given [param path] is configured to be synchronized on process.
+ [i]Deprecated.[/i] Use [method property_set_replication_mode] with [constant REPLICATION_MODE_ALWAYS] instead.
</description>
</method>
- <method name="property_set_watch">
+ <method name="property_set_watch" is_deprecated="true">
<return type="void" />
<param index="0" name="path" type="NodePath" />
<param index="1" name="enabled" type="bool" />
<description>
Sets whether the property identified by the given [param path] is configured to be reliably synchronized when changes are detected on process.
+ [i]Deprecated.[/i] Use [method property_set_replication_mode] with [constant REPLICATION_MODE_ON_CHANGE] instead.
</description>
</method>
<method name="remove_property">
@@ -90,4 +109,15 @@
</description>
</method>
</methods>
+ <constants>
+ <constant name="REPLICATION_MODE_NEVER" value="0" enum="ReplicationMode">
+ Do not keep the given property synchronized.
+ </constant>
+ <constant name="REPLICATION_MODE_ALWAYS" value="1" enum="ReplicationMode">
+ Replicate the given property on process by constantly sending updates using unreliable transfer mode.
+ </constant>
+ <constant name="REPLICATION_MODE_ON_CHANGE" value="2" enum="ReplicationMode">
+ Replicate the given property on process by sending updates using reliable transfer mode when its value changes.
+ </constant>
+ </constants>
</class>
diff --git a/modules/multiplayer/editor/editor_network_profiler.cpp b/modules/multiplayer/editor/editor_network_profiler.cpp
index c2cb0a3d1a..a53eefc452 100644
--- a/modules/multiplayer/editor/editor_network_profiler.cpp
+++ b/modules/multiplayer/editor/editor_network_profiler.cpp
@@ -33,6 +33,7 @@
#include "core/os/os.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/editor_string_names.h"
void EditorNetworkProfiler::_bind_methods() {
ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable")));
@@ -62,19 +63,19 @@ void EditorNetworkProfiler::_notification(int p_what) {
void EditorNetworkProfiler::_update_theme_item_cache() {
VBoxContainer::_update_theme_item_cache();
- theme_cache.node_icon = get_theme_icon(SNAME("Node"), SNAME("EditorIcons"));
- theme_cache.stop_icon = get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"));
- theme_cache.play_icon = get_theme_icon(SNAME("Play"), SNAME("EditorIcons"));
- theme_cache.clear_icon = get_theme_icon(SNAME("Clear"), SNAME("EditorIcons"));
+ theme_cache.node_icon = get_theme_icon(SNAME("Node"), EditorStringName(EditorIcons));
+ theme_cache.stop_icon = get_theme_icon(SNAME("Stop"), EditorStringName(EditorIcons));
+ theme_cache.play_icon = get_theme_icon(SNAME("Play"), EditorStringName(EditorIcons));
+ theme_cache.clear_icon = get_theme_icon(SNAME("Clear"), EditorStringName(EditorIcons));
- theme_cache.multiplayer_synchronizer_icon = get_theme_icon("MultiplayerSynchronizer", SNAME("EditorIcons"));
- theme_cache.instance_options_icon = get_theme_icon(SNAME("InstanceOptions"), SNAME("EditorIcons"));
+ theme_cache.multiplayer_synchronizer_icon = get_theme_icon("MultiplayerSynchronizer", EditorStringName(EditorIcons));
+ theme_cache.instance_options_icon = get_theme_icon(SNAME("InstanceOptions"), EditorStringName(EditorIcons));
- theme_cache.incoming_bandwidth_icon = get_theme_icon(SNAME("ArrowDown"), SNAME("EditorIcons"));
- theme_cache.outgoing_bandwidth_icon = get_theme_icon(SNAME("ArrowUp"), SNAME("EditorIcons"));
+ theme_cache.incoming_bandwidth_icon = get_theme_icon(SNAME("ArrowDown"), EditorStringName(EditorIcons));
+ theme_cache.outgoing_bandwidth_icon = get_theme_icon(SNAME("ArrowUp"), EditorStringName(EditorIcons));
- theme_cache.incoming_bandwidth_color = get_theme_color(SNAME("font_color"), SNAME("Editor"));
- theme_cache.outgoing_bandwidth_color = get_theme_color(SNAME("font_color"), SNAME("Editor"));
+ theme_cache.incoming_bandwidth_color = get_theme_color(SNAME("font_color"), EditorStringName(Editor));
+ theme_cache.outgoing_bandwidth_color = get_theme_color(SNAME("font_color"), EditorStringName(Editor));
}
void EditorNetworkProfiler::_refresh() {
@@ -128,7 +129,7 @@ void EditorNetworkProfiler::refresh_replication_data() {
const NodeInfo &cfg_info = node_data[E.value.config];
node->set_text(0, root_info.path.get_file());
- node->set_icon(0, has_theme_icon(root_info.type, SNAME("EditorIcons")) ? get_theme_icon(root_info.type, SNAME("EditorIcons")) : theme_cache.node_icon);
+ node->set_icon(0, has_theme_icon(root_info.type, EditorStringName(EditorIcons)) ? get_theme_icon(root_info.type, EditorStringName(EditorIcons)) : theme_cache.node_icon);
node->set_tooltip_text(0, root_info.path);
node->set_text(1, sync_info.path.get_file());
diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp
index 04aa856bf9..eab1f5d51d 100644
--- a/modules/multiplayer/editor/replication_editor.cpp
+++ b/modules/multiplayer/editor/replication_editor.cpp
@@ -35,6 +35,7 @@
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/scene_tree_editor.h"
#include "editor/inspector_dock.h"
@@ -111,9 +112,9 @@ void ReplicationEditor::_pick_node_filter_input(const Ref<InputEvent> &p_ie) {
void ReplicationEditor::_pick_node_selected(NodePath p_path) {
Node *root = current->get_node(current->get_root_path());
- ERR_FAIL_COND(!root);
+ ERR_FAIL_NULL(root);
Node *node = get_node(p_path);
- ERR_FAIL_COND(!node);
+ ERR_FAIL_NULL(node);
NodePath path_to = root->get_path_to(node);
adding_node_path = path_to;
prop_selector->select_property_from_instance(node);
@@ -176,11 +177,6 @@ ReplicationEditor::ReplicationEditor() {
delete_dialog->connect("confirmed", callable_mp(this, &ReplicationEditor::_dialog_closed).bind(true));
add_child(delete_dialog);
- error_dialog = memnew(AcceptDialog);
- error_dialog->set_ok_button_text(TTR("Close"));
- error_dialog->set_title(TTR("Error!"));
- add_child(error_dialog);
-
VBoxContainer *vb = memnew(VBoxContainer);
vb->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(vb);
@@ -249,42 +245,45 @@ ReplicationEditor::ReplicationEditor() {
add_pick_button->connect("pressed", callable_mp(this, &ReplicationEditor::_pick_new_property));
add_pick_button->set_text(TTR("Add property to sync..."));
hb->add_child(add_pick_button);
+
VSeparator *vs = memnew(VSeparator);
vs->set_custom_minimum_size(Size2(30 * EDSCALE, 0));
hb->add_child(vs);
hb->add_child(memnew(Label(TTR("Path:"))));
+
np_line_edit = memnew(LineEdit);
np_line_edit->set_placeholder(":property");
np_line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ np_line_edit->connect("text_submitted", callable_mp(this, &ReplicationEditor::_np_text_submitted));
hb->add_child(np_line_edit);
+
add_from_path_button = memnew(Button);
add_from_path_button->connect("pressed", callable_mp(this, &ReplicationEditor::_add_pressed));
add_from_path_button->set_text(TTR("Add from path"));
hb->add_child(add_from_path_button);
+
vs = memnew(VSeparator);
vs->set_custom_minimum_size(Size2(30 * EDSCALE, 0));
hb->add_child(vs);
+
pin = memnew(Button);
- pin->set_flat(true);
+ pin->set_theme_type_variation("FlatButton");
pin->set_toggle_mode(true);
hb->add_child(pin);
tree = memnew(Tree);
tree->set_hide_root(true);
- tree->set_columns(5);
+ tree->set_columns(4);
tree->set_column_titles_visible(true);
tree->set_column_title(0, TTR("Properties"));
tree->set_column_expand(0, true);
tree->set_column_title(1, TTR("Spawn"));
tree->set_column_expand(1, false);
tree->set_column_custom_minimum_width(1, 100);
- tree->set_column_title(2, TTR("Sync"));
+ tree->set_column_title(2, TTR("Replicate"));
tree->set_column_custom_minimum_width(2, 100);
- tree->set_column_title(3, TTR("Watch"));
- tree->set_column_custom_minimum_width(3, 100);
tree->set_column_expand(2, false);
tree->set_column_expand(3, false);
- tree->set_column_expand(4, false);
tree->create_item();
tree->connect("button_clicked", callable_mp(this, &ReplicationEditor::_tree_button_pressed));
tree->connect("item_edited", callable_mp(this, &ReplicationEditor::_tree_item_edited));
@@ -292,7 +291,7 @@ ReplicationEditor::ReplicationEditor() {
vb->add_child(tree);
drop_label = memnew(Label);
- drop_label->set_text(TTR("Add properties using the buttons above or\ndrag them them from the inspector and drop them here."));
+ drop_label->set_text(TTR("Add properties using the options above, or\ndrag them them from the inspector and drop them here."));
drop_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
drop_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
tree->add_child(drop_label);
@@ -303,7 +302,7 @@ ReplicationEditor::ReplicationEditor() {
void ReplicationEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_config"), &ReplicationEditor::_update_config);
- ClassDB::bind_method(D_METHOD("_update_checked", "property", "column", "checked"), &ReplicationEditor::_update_checked);
+ ClassDB::bind_method(D_METHOD("_update_value", "property", "column", "value"), &ReplicationEditor::_update_value);
}
bool ReplicationEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
@@ -365,25 +364,29 @@ void ReplicationEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("panel"), SNAME("Panel")));
- add_pick_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- pin->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons")));
+ add_theme_style_override("panel", EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("panel"), SNAME("Panel")));
+ add_pick_button->set_icon(get_theme_icon(SNAME("Add"), EditorStringName(EditorIcons)));
+ pin->set_icon(get_theme_icon(SNAME("Pin"), EditorStringName(EditorIcons)));
} break;
}
}
void ReplicationEditor::_add_pressed() {
if (!current) {
- error_dialog->set_text(TTR("Please select a MultiplayerSynchronizer first."));
- error_dialog->popup_centered();
+ EditorNode::get_singleton()->show_warning(TTR("Please select a MultiplayerSynchronizer first."));
return;
}
if (current->get_root_path().is_empty()) {
- error_dialog->set_text(TTR("The MultiplayerSynchronizer needs a root path."));
- error_dialog->popup_centered();
+ EditorNode::get_singleton()->show_warning(TTR("The MultiplayerSynchronizer needs a root path."));
return;
}
String np_text = np_line_edit->get_text();
+
+ if (np_text.is_empty()) {
+ EditorNode::get_singleton()->show_warning(TTR("Property/path must not be empty."));
+ return;
+ }
+
int idx = np_text.find(":");
if (idx == -1) {
np_text = ".:" + np_text;
@@ -391,46 +394,54 @@ void ReplicationEditor::_add_pressed() {
np_text = "." + np_text;
}
NodePath path = NodePath(np_text);
+ if (path.is_empty()) {
+ EditorNode::get_singleton()->show_warning(vformat(TTR("Invalid property path: '%s'"), np_text));
+ return;
+ }
_add_sync_property(path);
}
+void ReplicationEditor::_np_text_submitted(const String &p_newtext) {
+ _add_pressed();
+}
+
void ReplicationEditor::_tree_item_edited() {
TreeItem *ti = tree->get_edited();
if (!ti || config.is_null()) {
return;
}
int column = tree->get_edited_column();
- ERR_FAIL_COND(column < 1 || column > 3);
+ ERR_FAIL_COND(column < 1 || column > 2);
const NodePath prop = ti->get_metadata(0);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- bool value = ti->is_checked(column);
- // We have a hard limit of 64 watchable properties per synchronizer.
- if (column == 3 && value && config->get_watch_properties().size() > 64) {
- error_dialog->set_text(TTR("Each MultiplayerSynchronizer can have no more than 64 watched properties."));
- error_dialog->popup_centered();
- ti->set_checked(column, false);
- return;
- }
- String method;
if (column == 1) {
undo_redo->create_action(TTR("Set spawn property"));
- method = "property_set_spawn";
+ bool value = ti->is_checked(column);
+ undo_redo->add_do_method(config.ptr(), "property_set_spawn", prop, value);
+ undo_redo->add_undo_method(config.ptr(), "property_set_spawn", prop, !value);
+ undo_redo->add_do_method(this, "_update_value", prop, column, value ? 1 : 0);
+ undo_redo->add_undo_method(this, "_update_value", prop, column, value ? 1 : 0);
+ undo_redo->commit_action();
} else if (column == 2) {
undo_redo->create_action(TTR("Set sync property"));
- method = "property_set_sync";
- } else if (column == 3) {
- undo_redo->create_action(TTR("Set watch property"));
- method = "property_set_watch";
+ int value = ti->get_range(column);
+ int old_value = config->property_get_replication_mode(prop);
+ // We have a hard limit of 64 watchable properties per synchronizer.
+ if (value == SceneReplicationConfig::REPLICATION_MODE_ON_CHANGE && config->get_watch_properties().size() >= 64) {
+ EditorNode::get_singleton()->show_warning(TTR("Each MultiplayerSynchronizer can have no more than 64 watched properties."));
+ ti->set_range(column, old_value);
+ return;
+ }
+ undo_redo->add_do_method(config.ptr(), "property_set_replication_mode", prop, value);
+ undo_redo->add_undo_method(config.ptr(), "property_set_replication_mode", prop, old_value);
+ undo_redo->add_do_method(this, "_update_value", prop, column, value);
+ undo_redo->add_undo_method(this, "_update_value", prop, column, old_value);
+ undo_redo->commit_action();
} else {
ERR_FAIL();
}
- undo_redo->add_do_method(config.ptr(), method, prop, value);
- undo_redo->add_undo_method(config.ptr(), method, prop, !value);
- undo_redo->add_do_method(this, "_update_checked", prop, column, value);
- undo_redo->add_undo_method(this, "_update_checked", prop, column, !value);
- undo_redo->commit_action();
}
void ReplicationEditor::_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {
@@ -455,15 +466,13 @@ void ReplicationEditor::_dialog_closed(bool p_confirmed) {
const NodePath prop = deleting;
int idx = config->property_get_index(prop);
bool spawn = config->property_get_spawn(prop);
- bool sync = config->property_get_sync(prop);
- bool watch = config->property_get_watch(prop);
+ SceneReplicationConfig::ReplicationMode mode = config->property_get_replication_mode(prop);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Remove Property"));
undo_redo->add_do_method(config.ptr(), "remove_property", prop);
undo_redo->add_undo_method(config.ptr(), "add_property", prop, idx);
undo_redo->add_undo_method(config.ptr(), "property_set_spawn", prop, spawn);
- undo_redo->add_undo_method(config.ptr(), "property_set_sync", prop, sync);
- undo_redo->add_undo_method(config.ptr(), "property_set_watch", prop, watch);
+ undo_redo->add_undo_method(config.ptr(), "property_set_replication_mode", prop, mode);
undo_redo->add_do_method(this, "_update_config");
undo_redo->add_undo_method(this, "_update_config");
undo_redo->commit_action();
@@ -471,14 +480,18 @@ void ReplicationEditor::_dialog_closed(bool p_confirmed) {
deleting = NodePath();
}
-void ReplicationEditor::_update_checked(const NodePath &p_prop, int p_column, bool p_checked) {
+void ReplicationEditor::_update_value(const NodePath &p_prop, int p_column, int p_value) {
if (!tree->get_root()) {
return;
}
TreeItem *ti = tree->get_root()->get_first_child();
while (ti) {
if (ti->get_metadata(0).operator NodePath() == p_prop) {
- ti->set_checked(p_column, p_checked);
+ if (p_column == 1) {
+ ti->set_checked(p_column, p_value != 0);
+ } else if (p_column == 2) {
+ ti->set_range(p_column, p_value);
+ }
return;
}
ti = ti->get_next();
@@ -499,7 +512,7 @@ void ReplicationEditor::_update_config() {
}
for (int i = 0; i < props.size(); i++) {
const NodePath path = props[i];
- _add_property(path, config->property_get_spawn(path), config->property_get_sync(path), config->property_get_watch(path));
+ _add_property(path, config->property_get_spawn(path), config->property_get_replication_mode(path));
}
}
@@ -517,10 +530,10 @@ void ReplicationEditor::edit(MultiplayerSynchronizer *p_sync) {
}
Ref<Texture2D> ReplicationEditor::_get_class_icon(const Node *p_node) {
- if (!p_node || !has_theme_icon(p_node->get_class(), "EditorIcons")) {
- return get_theme_icon(SNAME("ImportFail"), SNAME("EditorIcons"));
+ if (!p_node || !has_theme_icon(p_node->get_class(), EditorStringName(EditorIcons))) {
+ return get_theme_icon(SNAME("ImportFail"), EditorStringName(EditorIcons));
}
- return get_theme_icon(p_node->get_class(), "EditorIcons");
+ return get_theme_icon(p_node->get_class(), EditorStringName(EditorIcons));
}
static bool can_sync(const Variant &p_var) {
@@ -541,14 +554,13 @@ static bool can_sync(const Variant &p_var) {
}
}
-void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn, bool p_sync, bool p_watch) {
+void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn, SceneReplicationConfig::ReplicationMode p_mode) {
String prop = String(p_property);
TreeItem *item = tree->create_item();
item->set_selectable(0, false);
item->set_selectable(1, false);
item->set_selectable(2, false);
item->set_selectable(3, false);
- item->set_selectable(4, false);
item->set_text(0, prop);
item->set_metadata(0, prop);
Node *root_node = current && !current->get_root_path().is_empty() ? current->get_node(current->get_root_path()) : nullptr;
@@ -565,22 +577,23 @@ void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn,
bool valid = false;
Variant value = node->get(subpath, &valid);
if (valid && !can_sync(value)) {
- item->set_icon(3, get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
- item->set_tooltip_text(3, TTR("Property of this type not supported."));
+ item->set_icon(0, get_theme_icon(SNAME("StatusWarning"), EditorStringName(EditorIcons)));
+ item->set_tooltip_text(0, TTR("Property of this type not supported."));
+ } else {
+ item->set_icon(0, icon);
}
+ } else {
+ item->set_icon(0, icon);
}
- item->set_icon(0, icon);
- item->add_button(4, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
+ item->add_button(3, get_theme_icon(SNAME("Remove"), EditorStringName(EditorIcons)));
item->set_text_alignment(1, HORIZONTAL_ALIGNMENT_CENTER);
item->set_cell_mode(1, TreeItem::CELL_MODE_CHECK);
item->set_checked(1, p_spawn);
item->set_editable(1, true);
item->set_text_alignment(2, HORIZONTAL_ALIGNMENT_CENTER);
- item->set_cell_mode(2, TreeItem::CELL_MODE_CHECK);
- item->set_checked(2, p_sync);
+ item->set_cell_mode(2, TreeItem::CELL_MODE_RANGE);
+ item->set_range_config(2, 0, 2, 1);
+ item->set_text(2, "Never,Always,On Change");
+ item->set_range(2, (int)p_mode);
item->set_editable(2, true);
- item->set_text_alignment(3, HORIZONTAL_ALIGNMENT_CENTER);
- item->set_cell_mode(3, TreeItem::CELL_MODE_CHECK);
- item->set_checked(3, p_watch);
- item->set_editable(3, true);
}
diff --git a/modules/multiplayer/editor/replication_editor.h b/modules/multiplayer/editor/replication_editor.h
index 208eaabff5..80c1892ec3 100644
--- a/modules/multiplayer/editor/replication_editor.h
+++ b/modules/multiplayer/editor/replication_editor.h
@@ -51,7 +51,6 @@ class ReplicationEditor : public VBoxContainer {
private:
MultiplayerSynchronizer *current = nullptr;
- AcceptDialog *error_dialog = nullptr;
ConfirmationDialog *delete_dialog = nullptr;
Button *add_pick_button = nullptr;
Button *add_from_path_button = nullptr;
@@ -72,12 +71,13 @@ private:
Ref<Texture2D> _get_class_icon(const Node *p_node);
void _add_pressed();
+ void _np_text_submitted(const String &p_newtext);
void _tree_item_edited();
void _tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
- void _update_checked(const NodePath &p_prop, int p_column, bool p_checked);
+ void _update_value(const NodePath &p_prop, int p_column, int p_checked);
void _update_config();
void _dialog_closed(bool p_confirmed);
- void _add_property(const NodePath &p_property, bool p_spawn = true, bool p_sync = true, bool p_watch = false);
+ void _add_property(const NodePath &p_property, bool p_spawn, SceneReplicationConfig::ReplicationMode p_mode);
void _pick_node_filter_text_changed(const String &p_newtext);
void _pick_node_select_recursive(TreeItem *p_item, const String &p_filter, Vector<Node *> &p_select_candidates);
diff --git a/modules/multiplayer/multiplayer_debugger.cpp b/modules/multiplayer/multiplayer_debugger.cpp
index ea52741601..9b05fa884b 100644
--- a/modules/multiplayer/multiplayer_debugger.cpp
+++ b/modules/multiplayer/multiplayer_debugger.cpp
@@ -237,7 +237,7 @@ void MultiplayerDebugger::RPCProfiler::tick(double p_frame_time, double p_proces
// ReplicationProfiler
MultiplayerDebugger::SyncInfo::SyncInfo(MultiplayerSynchronizer *p_sync) {
- ERR_FAIL_COND(!p_sync);
+ ERR_FAIL_NULL(p_sync);
synchronizer = p_sync->get_instance_id();
if (p_sync->get_replication_config().is_valid()) {
config = p_sync->get_replication_config()->get_instance_id();
@@ -305,7 +305,7 @@ void MultiplayerDebugger::ReplicationProfiler::add(const Array &p_data) {
const ObjectID id = p_data[1];
const uint64_t size = p_data[2];
MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(ObjectDB::get_instance(id));
- ERR_FAIL_COND(!sync);
+ ERR_FAIL_NULL(sync);
if (!sync_data.has(id)) {
sync_data[id] = SyncInfo(sync);
}
diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp
index 6c6aa28344..264a2e9c8e 100644
--- a/modules/multiplayer/multiplayer_spawner.cpp
+++ b/modules/multiplayer/multiplayer_spawner.cpp
@@ -268,7 +268,7 @@ void MultiplayerSpawner::_spawn_notify(ObjectID p_id) {
void MultiplayerSpawner::_node_exit(ObjectID p_id) {
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id));
- ERR_FAIL_COND(!node);
+ ERR_FAIL_NULL(node);
if (tracked_nodes.has(p_id)) {
tracked_nodes.erase(p_id);
get_multiplayer()->object_configuration_remove(node, this);
@@ -323,10 +323,10 @@ Node *MultiplayerSpawner::spawn(const Variant &p_data) {
ERR_FAIL_COND_V_MSG(!spawn_function.is_valid(), nullptr, "Custom spawn requires the 'spawn_function' property to be a valid callable.");
Node *parent = get_spawn_node();
- ERR_FAIL_COND_V_MSG(!parent, nullptr, "Cannot find spawn node.");
+ ERR_FAIL_NULL_V_MSG(parent, nullptr, "Cannot find spawn node.");
Node *node = instantiate_custom(p_data);
- ERR_FAIL_COND_V_MSG(!node, nullptr, "The 'spawn_function' callable must return a valid node.");
+ ERR_FAIL_NULL_V_MSG(node, nullptr, "The 'spawn_function' callable must return a valid node.");
_track(node, p_data);
parent->add_child(node, true);
diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp
index 9c2d281f72..233f15c3a4 100644
--- a/modules/multiplayer/multiplayer_synchronizer.cpp
+++ b/modules/multiplayer/multiplayer_synchronizer.cpp
@@ -149,14 +149,14 @@ PackedStringArray MultiplayerSynchronizer::get_configuration_warnings() const {
}
Error MultiplayerSynchronizer::get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs) {
- ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_obj, ERR_INVALID_PARAMETER);
r_variant.resize(p_properties.size());
r_variant_ptrs.resize(r_variant.size());
int i = 0;
for (const NodePath &prop : p_properties) {
bool valid = false;
const Object *obj = _get_prop_target(p_obj, prop);
- ERR_FAIL_COND_V(!obj, FAILED);
+ ERR_FAIL_NULL_V(obj, FAILED);
r_variant.write[i] = obj->get_indexed(prop.get_subnames(), &valid);
r_variant_ptrs.write[i] = &r_variant[i];
ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop));
@@ -166,11 +166,11 @@ Error MultiplayerSynchronizer::get_state(const List<NodePath> &p_properties, Obj
}
Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state) {
- ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_obj, ERR_INVALID_PARAMETER);
int i = 0;
for (const NodePath &prop : p_properties) {
Object *obj = _get_prop_target(p_obj, prop);
- ERR_FAIL_COND_V(!obj, FAILED);
+ ERR_FAIL_NULL_V(obj, FAILED);
obj->set_indexed(prop.get_subnames(), p_state[i]);
i += 1;
}
@@ -374,7 +374,7 @@ Error MultiplayerSynchronizer::_watch_changes(uint64_t p_usec) {
return OK;
}
Node *node = get_root_node();
- ERR_FAIL_COND_V(!node, FAILED);
+ ERR_FAIL_NULL_V(node, FAILED);
int idx = -1;
Watcher *ptr = watchers.ptrw();
for (const NodePath &prop : props) {
diff --git a/modules/multiplayer/scene_cache_interface.cpp b/modules/multiplayer/scene_cache_interface.cpp
index 90e6ac7c2c..8d102ca981 100644
--- a/modules/multiplayer/scene_cache_interface.cpp
+++ b/modules/multiplayer/scene_cache_interface.cpp
@@ -53,7 +53,7 @@ void SceneCacheInterface::on_peer_change(int p_id, bool p_connected) {
void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());
- ERR_FAIL_COND(!root_node);
+ ERR_FAIL_NULL(root_node);
ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small.");
int ofs = 1;
@@ -74,7 +74,7 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac
}
Node *node = root_node->get_node(path);
- ERR_FAIL_COND(node == nullptr);
+ ERR_FAIL_NULL(node);
const bool valid_rpc_checksum = multiplayer->get_rpc_md5(node) == methods_md5;
if (valid_rpc_checksum == false) {
ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
@@ -119,7 +119,7 @@ void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_pack
}
PathSentCache *psc = path_send_cache.getptr(path);
- ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache.");
+ ERR_FAIL_NULL_MSG(psc, "Invalid packet received. Tries to confirm a path which was not found in cache.");
HashMap<int, bool>::Iterator E = psc->confirmed_peers.find(p_from);
ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path.");
@@ -165,7 +165,7 @@ Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, Pat
bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) {
const PathSentCache *psc = path_send_cache.getptr(p_path);
- ERR_FAIL_COND_V(!psc, false);
+ ERR_FAIL_NULL_V(psc, false);
HashMap<int, bool>::ConstIterator F = psc->confirmed_peers.find(p_peer);
ERR_FAIL_COND_V(!F, false); // Should never happen.
return F->value;
@@ -173,7 +173,7 @@ bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) {
int SceneCacheInterface::make_object_cache(Object *p_obj) {
Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V(!node, -1);
+ ERR_FAIL_NULL_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(for_path);
@@ -188,7 +188,7 @@ int SceneCacheInterface::make_object_cache(Object *p_obj) {
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);
+ ERR_FAIL_NULL_V(node, false);
r_id = make_object_cache(p_obj);
ERR_FAIL_COND_V(r_id < 0, false);
@@ -233,7 +233,7 @@ bool SceneCacheInterface::send_object_cache(Object *p_obj, int p_peer_id, int &r
Object *SceneCacheInterface::get_cached_object(int p_from, uint32_t p_cache_id) {
Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());
- ERR_FAIL_COND_V(!root_node, nullptr);
+ ERR_FAIL_NULL_V(root_node, nullptr);
HashMap<int, PathGetCache>::Iterator E = path_get_cache.find(p_from);
ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from));
diff --git a/modules/multiplayer/scene_cache_interface.h b/modules/multiplayer/scene_cache_interface.h
index 9400417cdb..7a7304fde8 100644
--- a/modules/multiplayer/scene_cache_interface.h
+++ b/modules/multiplayer/scene_cache_interface.h
@@ -33,6 +33,7 @@
#include "scene/main/multiplayer_api.h"
+class Node;
class SceneMultiplayer;
class SceneCacheInterface : public RefCounted {
diff --git a/modules/multiplayer/scene_replication_config.cpp b/modules/multiplayer/scene_replication_config.cpp
index af6af35219..836fa1014d 100644
--- a/modules/multiplayer/scene_replication_config.cpp
+++ b/modules/multiplayer/scene_replication_config.cpp
@@ -47,38 +47,26 @@ bool SceneReplicationConfig::_set(const StringName &p_name, const Variant &p_val
add_property(path);
return true;
}
- ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false);
ERR_FAIL_INDEX_V(idx, properties.size(), false);
ReplicationProperty &prop = properties[idx];
- if (what == "sync") {
- if ((bool)p_value == prop.sync) {
- return true;
- }
- prop.sync = p_value;
- if (prop.sync) {
- sync_props.push_back(prop.name);
- } else {
- sync_props.erase(prop.name);
- }
+ if (what == "replication_mode") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ ReplicationMode mode = (ReplicationMode)p_value.operator int();
+ ERR_FAIL_COND_V(mode < REPLICATION_MODE_NEVER || mode > REPLICATION_MODE_ON_CHANGE, false);
+ property_set_replication_mode(prop.name, mode);
return true;
- } else if (what == "spawn") {
- if ((bool)p_value == prop.spawn) {
- return true;
- }
- prop.spawn = p_value;
- if (prop.spawn) {
- spawn_props.push_back(prop.name);
- } else {
- spawn_props.erase(prop.name);
- }
+ }
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false);
+ if (what == "spawn") {
+ property_set_spawn(prop.name, p_value);
+ return true;
+ } else if (what == "sync") {
+ // Deprecated.
+ property_set_sync(prop.name, p_value);
return true;
} else if (what == "watch") {
- prop.watch = p_value;
- if (prop.watch) {
- watch_props.push_back(prop.name);
- } else {
- watch_props.erase(prop.name);
- }
+ // Deprecated.
+ property_set_watch(prop.name, p_value);
return true;
}
}
@@ -96,14 +84,11 @@ bool SceneReplicationConfig::_get(const StringName &p_name, Variant &r_ret) cons
if (what == "path") {
r_ret = prop.name;
return true;
- } else if (what == "sync") {
- r_ret = prop.sync;
- return true;
} else if (what == "spawn") {
r_ret = prop.spawn;
return true;
- } else if (what == "watch") {
- r_ret = prop.watch;
+ } else if (what == "replication_mode") {
+ r_ret = prop.mode;
return true;
}
}
@@ -114,8 +99,7 @@ void SceneReplicationConfig::_get_property_list(List<PropertyInfo> *p_list) cons
for (int i = 0; i < properties.size(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/spawn", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/watch", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::INT, "properties/" + itos(i) + "/replication_mode", PROPERTY_HINT_ENUM, "Never,Always,On Change", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
}
@@ -129,11 +113,11 @@ TypedArray<NodePath> SceneReplicationConfig::get_properties() const {
void SceneReplicationConfig::add_property(const NodePath &p_path, int p_index) {
ERR_FAIL_COND(properties.find(p_path));
+ ERR_FAIL_COND(p_path == NodePath());
if (p_index < 0 || p_index == properties.size()) {
properties.push_back(ReplicationProperty(p_path));
- sync_props.push_back(p_path);
- spawn_props.push_back(p_path);
+ dirty = true;
return;
}
@@ -146,22 +130,12 @@ void SceneReplicationConfig::add_property(const NodePath &p_path, int p_index) {
c++;
}
properties.insert_before(I, ReplicationProperty(p_path));
- sync_props.clear();
- spawn_props.clear();
- for (const ReplicationProperty &prop : properties) {
- if (prop.sync) {
- sync_props.push_back(prop.name);
- }
- if (prop.spawn) {
- spawn_props.push_back(prop.name);
- }
- }
+ dirty = true;
}
void SceneReplicationConfig::remove_property(const NodePath &p_path) {
properties.erase(p_path);
- sync_props.erase(p_path);
- spawn_props.erase(p_path);
+ dirty = true;
}
bool SceneReplicationConfig::has_property(const NodePath &p_path) const {
@@ -195,56 +169,99 @@ void SceneReplicationConfig::property_set_spawn(const NodePath &p_path, bool p_e
return;
}
E->get().spawn = p_enabled;
- spawn_props.clear();
- for (const ReplicationProperty &prop : properties) {
- if (prop.spawn) {
- spawn_props.push_back(prop.name);
- }
- }
+ dirty = true;
}
bool SceneReplicationConfig::property_get_sync(const NodePath &p_path) {
List<ReplicationProperty>::Element *E = properties.find(p_path);
ERR_FAIL_COND_V(!E, false);
- return E->get().sync;
+ return E->get().mode == REPLICATION_MODE_ALWAYS;
}
void SceneReplicationConfig::property_set_sync(const NodePath &p_path, bool p_enabled) {
- List<ReplicationProperty>::Element *E = properties.find(p_path);
- ERR_FAIL_COND(!E);
- if (E->get().sync == p_enabled) {
- return;
- }
- E->get().sync = p_enabled;
- sync_props.clear();
- for (const ReplicationProperty &prop : properties) {
- if (prop.sync) {
- sync_props.push_back(prop.name);
- }
+ if (p_enabled) {
+ property_set_replication_mode(p_path, REPLICATION_MODE_ALWAYS);
+ } else if (property_get_replication_mode(p_path) == REPLICATION_MODE_ALWAYS) {
+ property_set_replication_mode(p_path, REPLICATION_MODE_NEVER);
}
}
bool SceneReplicationConfig::property_get_watch(const NodePath &p_path) {
List<ReplicationProperty>::Element *E = properties.find(p_path);
ERR_FAIL_COND_V(!E, false);
- return E->get().watch;
+ return E->get().mode == REPLICATION_MODE_ON_CHANGE;
}
void SceneReplicationConfig::property_set_watch(const NodePath &p_path, bool p_enabled) {
+ if (p_enabled) {
+ property_set_replication_mode(p_path, REPLICATION_MODE_ON_CHANGE);
+ } else if (property_get_replication_mode(p_path) == REPLICATION_MODE_ON_CHANGE) {
+ property_set_replication_mode(p_path, REPLICATION_MODE_NEVER);
+ }
+}
+
+SceneReplicationConfig::ReplicationMode SceneReplicationConfig::property_get_replication_mode(const NodePath &p_path) {
+ List<ReplicationProperty>::Element *E = properties.find(p_path);
+ ERR_FAIL_COND_V(!E, REPLICATION_MODE_NEVER);
+ return E->get().mode;
+}
+
+void SceneReplicationConfig::property_set_replication_mode(const NodePath &p_path, ReplicationMode p_mode) {
List<ReplicationProperty>::Element *E = properties.find(p_path);
ERR_FAIL_COND(!E);
- if (E->get().watch == p_enabled) {
+ if (E->get().mode == p_mode) {
return;
}
- E->get().watch = p_enabled;
+ E->get().mode = p_mode;
+ dirty = true;
+}
+
+void SceneReplicationConfig::_update() {
+ if (!dirty) {
+ return;
+ }
+ dirty = false;
+ sync_props.clear();
+ spawn_props.clear();
watch_props.clear();
for (const ReplicationProperty &prop : properties) {
- if (prop.watch) {
- watch_props.push_back(p_path);
+ if (prop.spawn) {
+ spawn_props.push_back(prop.name);
+ }
+ switch (prop.mode) {
+ case REPLICATION_MODE_ALWAYS:
+ sync_props.push_back(prop.name);
+ break;
+ case REPLICATION_MODE_ON_CHANGE:
+ watch_props.push_back(prop.name);
+ break;
+ default:
+ break;
}
}
}
+const List<NodePath> &SceneReplicationConfig::get_spawn_properties() {
+ if (dirty) {
+ _update();
+ }
+ return spawn_props;
+}
+
+const List<NodePath> &SceneReplicationConfig::get_sync_properties() {
+ if (dirty) {
+ _update();
+ }
+ return sync_props;
+}
+
+const List<NodePath> &SceneReplicationConfig::get_watch_properties() {
+ if (dirty) {
+ _update();
+ }
+ return watch_props;
+}
+
void SceneReplicationConfig::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_properties"), &SceneReplicationConfig::get_properties);
ClassDB::bind_method(D_METHOD("add_property", "path", "index"), &SceneReplicationConfig::add_property, DEFVAL(-1));
@@ -253,6 +270,14 @@ void SceneReplicationConfig::_bind_methods() {
ClassDB::bind_method(D_METHOD("property_get_index", "path"), &SceneReplicationConfig::property_get_index);
ClassDB::bind_method(D_METHOD("property_get_spawn", "path"), &SceneReplicationConfig::property_get_spawn);
ClassDB::bind_method(D_METHOD("property_set_spawn", "path", "enabled"), &SceneReplicationConfig::property_set_spawn);
+ ClassDB::bind_method(D_METHOD("property_get_replication_mode", "path"), &SceneReplicationConfig::property_get_replication_mode);
+ ClassDB::bind_method(D_METHOD("property_set_replication_mode", "path", "mode"), &SceneReplicationConfig::property_set_replication_mode);
+
+ BIND_ENUM_CONSTANT(REPLICATION_MODE_NEVER);
+ BIND_ENUM_CONSTANT(REPLICATION_MODE_ALWAYS);
+ BIND_ENUM_CONSTANT(REPLICATION_MODE_ON_CHANGE);
+
+ // Deprecated.
ClassDB::bind_method(D_METHOD("property_get_sync", "path"), &SceneReplicationConfig::property_get_sync);
ClassDB::bind_method(D_METHOD("property_set_sync", "path", "enabled"), &SceneReplicationConfig::property_set_sync);
ClassDB::bind_method(D_METHOD("property_get_watch", "path"), &SceneReplicationConfig::property_get_watch);
diff --git a/modules/multiplayer/scene_replication_config.h b/modules/multiplayer/scene_replication_config.h
index 44f8259904..3f870ba2d8 100644
--- a/modules/multiplayer/scene_replication_config.h
+++ b/modules/multiplayer/scene_replication_config.h
@@ -39,12 +39,18 @@ class SceneReplicationConfig : public Resource {
OBJ_SAVE_TYPE(SceneReplicationConfig);
RES_BASE_EXTENSION("repl");
+public:
+ enum ReplicationMode {
+ REPLICATION_MODE_NEVER,
+ REPLICATION_MODE_ALWAYS,
+ REPLICATION_MODE_ON_CHANGE,
+ };
+
private:
struct ReplicationProperty {
NodePath name;
bool spawn = true;
- bool sync = true;
- bool watch = false;
+ ReplicationMode mode = REPLICATION_MODE_ALWAYS;
bool operator==(const ReplicationProperty &p_to) {
return name == p_to.name;
@@ -61,6 +67,9 @@ private:
List<NodePath> spawn_props;
List<NodePath> sync_props;
List<NodePath> watch_props;
+ bool dirty = false;
+
+ void _update();
protected:
static void _bind_methods();
@@ -86,11 +95,16 @@ public:
bool property_get_watch(const NodePath &p_path);
void property_set_watch(const NodePath &p_path, bool p_enabled);
- const List<NodePath> &get_spawn_properties() { return spawn_props; }
- const List<NodePath> &get_sync_properties() { return sync_props; }
- const List<NodePath> &get_watch_properties() { return watch_props; }
+ ReplicationMode property_get_replication_mode(const NodePath &p_path);
+ void property_set_replication_mode(const NodePath &p_path, ReplicationMode p_mode);
+
+ const List<NodePath> &get_spawn_properties();
+ const List<NodePath> &get_sync_properties();
+ const List<NodePath> &get_watch_properties();
SceneReplicationConfig() {}
};
+VARIANT_ENUM_CAST(SceneReplicationConfig::ReplicationMode);
+
#endif // SCENE_REPLICATION_CONFIG_H
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
index 7f12fea4bf..e350f2f68b 100644
--- a/modules/multiplayer/scene_replication_interface.cpp
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -155,7 +155,7 @@ Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) {
Node *node = Object::cast_to<Node>(p_obj);
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);
+ ERR_FAIL_NULL_V(spawner, ERR_INVALID_PARAMETER);
// Track node.
const ObjectID oid = node->get_instance_id();
TrackedNode &tobj = _track(oid);
@@ -226,7 +226,7 @@ Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_c
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(!sync, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(sync, ERR_INVALID_PARAMETER);
// Add to synchronizer list.
TrackedNode &tobj = _track(p_obj->get_instance_id());
@@ -270,7 +270,7 @@ 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(!sync, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(sync, ERR_INVALID_PARAMETER);
sync->disconnect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed));
// Untrack synchronizer.
const ObjectID oid = node->get_instance_id();
@@ -291,9 +291,9 @@ Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_co
void SceneReplicationInterface::_visibility_changed(int p_peer, ObjectID p_sid) {
MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(p_sid);
- ERR_FAIL_COND(!sync); // Bug.
+ ERR_FAIL_NULL(sync); // Bug.
Node *node = sync->get_root_node();
- ERR_FAIL_COND(!node); // Bug.
+ ERR_FAIL_NULL(node); // Bug.
const ObjectID oid = node->get_instance_id();
if (spawned_nodes.has(oid) && p_peer != multiplayer->get_unique_id()) {
_update_spawn_visibility(p_peer, oid);
@@ -341,7 +341,7 @@ bool SceneReplicationInterface::is_rpc_visible(const ObjectID &p_oid, int p_peer
}
Error SceneReplicationInterface::_update_sync_visibility(int p_peer, MultiplayerSynchronizer *p_sync) {
- ERR_FAIL_COND_V(!p_sync, ERR_BUG);
+ ERR_FAIL_NULL_V(p_sync, ERR_BUG);
if (!multiplayer->has_multiplayer_peer() || !p_sync->is_multiplayer_authority() || p_peer == multiplayer->get_unique_id()) {
return OK;
}
@@ -380,7 +380,7 @@ Error SceneReplicationInterface::_update_sync_visibility(int p_peer, Multiplayer
Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const ObjectID &p_oid) {
const TrackedNode *tnode = tracked_nodes.getptr(p_oid);
- ERR_FAIL_COND_V(!tnode, ERR_BUG);
+ ERR_FAIL_NULL_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);
@@ -467,7 +467,7 @@ Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, MultiplayerSpa
const ObjectID oid = p_node->get_instance_id();
const TrackedNode *tnode = tracked_nodes.getptr(oid);
- ERR_FAIL_COND_V(!tnode, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(tnode, ERR_INVALID_PARAMETER);
uint32_t nid = tnode->net_id;
ERR_FAIL_COND_V(!nid, ERR_UNCONFIGURED);
@@ -549,7 +549,7 @@ Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, MultiplayerSpa
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);
+ ERR_FAIL_NULL_V(tnode, ERR_INVALID_PARAMETER);
MAKE_ROOM(5);
uint8_t *ptr = packet_cache.ptrw();
ptr[0] = (uint8_t)SceneMultiplayer::NETWORK_COMMAND_DESPAWN;
@@ -568,7 +568,7 @@ Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_b
uint32_t node_target = decode_uint32(&p_buffer[ofs]);
ofs += 4;
MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(multiplayer->get_path_cache()->get_cached_object(p_from, node_target));
- ERR_FAIL_COND_V(!spawner, ERR_DOES_NOT_EXIST);
+ ERR_FAIL_NULL_V(spawner, ERR_DOES_NOT_EXIST);
ERR_FAIL_COND_V(p_from != spawner->get_multiplayer_authority(), ERR_UNAUTHORIZED);
uint32_t net_id = decode_uint32(&p_buffer[ofs]);
@@ -592,7 +592,7 @@ Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_b
// Check that we can spawn.
Node *parent = spawner->get_node_or_null(spawner->get_spawn_path());
- ERR_FAIL_COND_V(!parent, ERR_UNCONFIGURED);
+ ERR_FAIL_NULL_V(parent, ERR_UNCONFIGURED);
ERR_FAIL_COND_V(parent->has_node(name), ERR_INVALID_DATA);
Node *node = nullptr;
@@ -611,7 +611,7 @@ Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_b
// Scene based spawn.
node = spawner->instantiate_scene(scene_id);
}
- ERR_FAIL_COND_V(!node, ERR_UNAUTHORIZED);
+ ERR_FAIL_NULL_V(node, ERR_UNAUTHORIZED);
node->set_name(name);
// Add and track remote
@@ -656,13 +656,13 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p
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);
+ ERR_FAIL_NULL_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_NULL_V(spawner, ERR_DOES_NOT_EXIST);
ERR_FAIL_COND_V(p_from != spawner->get_multiplayer_authority(), ERR_UNAUTHORIZED);
if (node->get_parent() != nullptr) {
diff --git a/modules/multiplayer/scene_rpc_interface.cpp b/modules/multiplayer/scene_rpc_interface.cpp
index da1a044c9d..48e1d13f9c 100644
--- a/modules/multiplayer/scene_rpc_interface.cpp
+++ b/modules/multiplayer/scene_rpc_interface.cpp
@@ -134,7 +134,7 @@ _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, i
String SceneRPCInterface::get_rpc_md5(const Object *p_obj) {
const Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V(!node, "");
+ ERR_FAIL_NULL_V(node, "");
const RPCConfigCache cache = _get_node_config(node);
String rpc_list;
for (const KeyValue<uint16_t, RPCConfig> &config : cache.configs) {
@@ -145,7 +145,7 @@ String SceneRPCInterface::get_rpc_md5(const Object *p_obj) {
Node *SceneRPCInterface::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) {
Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());
- ERR_FAIL_COND_V(!root_node, nullptr);
+ ERR_FAIL_NULL_V(root_node, nullptr);
Node *node = nullptr;
if (p_node_target & 0x80000000) {
@@ -225,7 +225,7 @@ void SceneRPCInterface::process_rpc(int p_from, const uint8_t *p_packet, int p_p
}
Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len);
- ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found.");
+ ERR_FAIL_NULL_MSG(node, "Invalid packet received. Requested node was not found.");
uint16_t name_id = 0;
switch (name_id_compression) {