Merge pull request #75467 from Faless/mp/4.x_watch
[MP] Implement "watched" properties (reliable/on change).
This commit is contained in:
commit
513f43e774
|
@ -51,6 +51,9 @@
|
|||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="delta_interval" type="float" setter="set_delta_interval" getter="get_delta_interval" default="0.0">
|
||||
Time interval between delta synchronizations. When set to [code]0.0[/code] (the default), delta synchronizations happen every network process frame.
|
||||
</member>
|
||||
<member name="public_visibility" type="bool" setter="set_visibility_public" getter="is_visibility_public" default="true">
|
||||
Whether synchronization should be visible to all peers by default. See [method set_visibility_for] and [method add_visibility_filter] for ways of configuring fine-grained visibility options.
|
||||
</member>
|
||||
|
@ -58,7 +61,7 @@
|
|||
Resource containing which properties to synchronize.
|
||||
</member>
|
||||
<member name="replication_interval" type="float" setter="set_replication_interval" getter="get_replication_interval" default="0.0">
|
||||
Time interval between synchronizes. When set to [code]0.0[/code] (the default), synchronizes happen every network process frame.
|
||||
Time interval between synchronizations. When set to [code]0.0[/code] (the default), synchronizations happen every network process frame.
|
||||
</member>
|
||||
<member name="root_path" type="NodePath" setter="set_root_path" getter="get_root_path" default="NodePath("..")">
|
||||
Node path that replicated properties are relative to.
|
||||
|
@ -69,9 +72,14 @@
|
|||
</member>
|
||||
</members>
|
||||
<signals>
|
||||
<signal name="delta_synchronized">
|
||||
<description>
|
||||
Emitted when a new delta synchronization state is received by this synchronizer after the properties have been updated.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="synchronized">
|
||||
<description>
|
||||
Emitted when a new synchronization state is received by this synchronizer after the variables have been updated.
|
||||
Emitted when a new synchronization state is received by this synchronizer after the properties have been updated.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="visibility_changed">
|
||||
|
|
|
@ -70,6 +70,12 @@
|
|||
<member name="auth_timeout" type="float" setter="set_auth_timeout" getter="get_auth_timeout" default="3.0">
|
||||
If set to a value greater than [code]0.0[/code], the maximum amount of time peers can stay in the authenticating state, after which the authentication will automatically fail. See the [signal peer_authenticating] and [signal peer_authentication_failed] signals.
|
||||
</member>
|
||||
<member name="max_delta_packet_size" type="int" setter="set_max_delta_packet_size" getter="get_max_delta_packet_size" default="65535">
|
||||
Maximum size of each delta packet. Higher values increase the chance of receiving full updates in a single frame, but also the chance of causing networking congestion (higher latency, disconnections). See [MultiplayerSynchronizer].
|
||||
</member>
|
||||
<member name="max_sync_packet_size" type="int" setter="set_max_sync_packet_size" getter="get_max_sync_packet_size" default="1350">
|
||||
Maximum size of each synchronization packet. Higher values increase the chance of receiving full updates in a single frame, but also the chance of packet loss. See [MultiplayerSynchronizer].
|
||||
</member>
|
||||
<member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" default="false">
|
||||
If [code]true[/code], the MultiplayerAPI's [member MultiplayerAPI.multiplayer_peer] refuses new incoming connections.
|
||||
</member>
|
||||
|
|
|
@ -50,6 +50,13 @@
|
|||
Returns whether the property identified by the given [param path] is configured to be synchronized on process.
|
||||
</description>
|
||||
</method>
|
||||
<method name="property_get_watch">
|
||||
<return type="bool" />
|
||||
<param index="0" name="path" type="NodePath" />
|
||||
<description>
|
||||
Returns whether the property identified by the given [code]path[/code] is configured to be reliably synchronized when changes are detected on process.
|
||||
</description>
|
||||
</method>
|
||||
<method name="property_set_spawn">
|
||||
<return type="void" />
|
||||
<param index="0" name="path" type="NodePath" />
|
||||
|
@ -66,6 +73,14 @@
|
|||
Sets whether the property identified by the given [param path] is configured to be synchronized on process.
|
||||
</description>
|
||||
</method>
|
||||
<method name="property_set_watch">
|
||||
<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 [code]path[/code] is configured to be reliably synchronized when changes are detected on process.
|
||||
</description>
|
||||
</method>
|
||||
<method name="remove_property">
|
||||
<return type="void" />
|
||||
<param index="0" name="path" type="NodePath" />
|
||||
|
|
|
@ -226,7 +226,7 @@ ReplicationEditor::ReplicationEditor() {
|
|||
|
||||
tree = memnew(Tree);
|
||||
tree->set_hide_root(true);
|
||||
tree->set_columns(4);
|
||||
tree->set_columns(5);
|
||||
tree->set_column_titles_visible(true);
|
||||
tree->set_column_title(0, TTR("Properties"));
|
||||
tree->set_column_expand(0, true);
|
||||
|
@ -235,8 +235,11 @@ ReplicationEditor::ReplicationEditor() {
|
|||
tree->set_column_custom_minimum_width(1, 100);
|
||||
tree->set_column_title(2, TTR("Sync"));
|
||||
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));
|
||||
|
@ -353,17 +356,30 @@ void ReplicationEditor::_tree_item_edited() {
|
|||
return;
|
||||
}
|
||||
int column = tree->get_edited_column();
|
||||
ERR_FAIL_COND(column < 1 || column > 2);
|
||||
ERR_FAIL_COND(column < 1 || column > 3);
|
||||
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";
|
||||
} else {
|
||||
} 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";
|
||||
} else {
|
||||
ERR_FAIL();
|
||||
}
|
||||
undo_redo->add_do_method(config.ptr(), method, prop, value);
|
||||
undo_redo->add_undo_method(config.ptr(), method, prop, !value);
|
||||
|
@ -395,12 +411,14 @@ void ReplicationEditor::_dialog_closed(bool p_confirmed) {
|
|||
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);
|
||||
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_do_method(this, "_update_config");
|
||||
undo_redo->add_undo_method(this, "_update_config");
|
||||
undo_redo->commit_action();
|
||||
|
@ -436,7 +454,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));
|
||||
_add_property(path, config->property_get_spawn(path), config->property_get_sync(path), config->property_get_watch(path));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -460,13 +478,14 @@ Ref<Texture2D> ReplicationEditor::_get_class_icon(const Node *p_node) {
|
|||
return get_theme_icon(p_node->get_class(), "EditorIcons");
|
||||
}
|
||||
|
||||
void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn, bool p_sync) {
|
||||
void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn, bool p_sync, bool p_watch) {
|
||||
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;
|
||||
|
@ -482,7 +501,7 @@ void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn,
|
|||
icon = _get_class_icon(node);
|
||||
}
|
||||
item->set_icon(0, icon);
|
||||
item->add_button(3, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
|
||||
item->add_button(4, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
|
||||
item->set_text_alignment(1, HORIZONTAL_ALIGNMENT_CENTER);
|
||||
item->set_cell_mode(1, TreeItem::CELL_MODE_CHECK);
|
||||
item->set_checked(1, p_spawn);
|
||||
|
@ -491,4 +510,8 @@ void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn,
|
|||
item->set_cell_mode(2, TreeItem::CELL_MODE_CHECK);
|
||||
item->set_checked(2, p_sync);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ private:
|
|||
void _update_checked(const NodePath &p_prop, int p_column, bool 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);
|
||||
void _add_property(const NodePath &p_property, bool p_spawn = true, bool p_sync = true, bool p_watch = false);
|
||||
|
||||
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);
|
||||
|
|
|
@ -105,7 +105,7 @@ Node *MultiplayerSynchronizer::get_root_node() {
|
|||
|
||||
void MultiplayerSynchronizer::reset() {
|
||||
net_id = 0;
|
||||
last_sync_msec = 0;
|
||||
last_sync_usec = 0;
|
||||
last_inbound_sync = 0;
|
||||
}
|
||||
|
||||
|
@ -117,16 +117,17 @@ void MultiplayerSynchronizer::set_net_id(uint32_t p_net_id) {
|
|||
net_id = p_net_id;
|
||||
}
|
||||
|
||||
bool MultiplayerSynchronizer::update_outbound_sync_time(uint64_t p_msec) {
|
||||
if (last_sync_msec == p_msec) {
|
||||
// last_sync_msec has been updated on this frame.
|
||||
bool MultiplayerSynchronizer::update_outbound_sync_time(uint64_t p_usec) {
|
||||
if (last_sync_usec == p_usec) {
|
||||
// last_sync_usec has been updated in this frame.
|
||||
return true;
|
||||
}
|
||||
if (p_msec >= last_sync_msec + interval_msec) {
|
||||
last_sync_msec = p_msec;
|
||||
return true;
|
||||
if (p_usec < last_sync_usec + sync_interval_usec) {
|
||||
// Too soon, should skip this synchronization frame.
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
last_sync_usec = p_usec;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MultiplayerSynchronizer::update_inbound_sync_time(uint16_t p_network_time) {
|
||||
|
@ -243,6 +244,9 @@ void MultiplayerSynchronizer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_replication_interval", "milliseconds"), &MultiplayerSynchronizer::set_replication_interval);
|
||||
ClassDB::bind_method(D_METHOD("get_replication_interval"), &MultiplayerSynchronizer::get_replication_interval);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_delta_interval", "milliseconds"), &MultiplayerSynchronizer::set_delta_interval);
|
||||
ClassDB::bind_method(D_METHOD("get_delta_interval"), &MultiplayerSynchronizer::get_delta_interval);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -260,6 +264,7 @@ void MultiplayerSynchronizer::_bind_methods() {
|
|||
|
||||
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::FLOAT, "delta_interval", PROPERTY_HINT_RANGE, "0,5,0.001,suffix:s"), "set_delta_interval", "get_delta_interval");
|
||||
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");
|
||||
|
@ -269,6 +274,7 @@ void MultiplayerSynchronizer::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_NONE);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("synchronized"));
|
||||
ADD_SIGNAL(MethodInfo("delta_synchronized"));
|
||||
ADD_SIGNAL(MethodInfo("visibility_changed", PropertyInfo(Variant::INT, "for_peer")));
|
||||
}
|
||||
|
||||
|
@ -300,11 +306,20 @@ void MultiplayerSynchronizer::_notification(int p_what) {
|
|||
|
||||
void MultiplayerSynchronizer::set_replication_interval(double p_interval) {
|
||||
ERR_FAIL_COND_MSG(p_interval < 0, "Interval must be greater or equal to 0 (where 0 means default)");
|
||||
interval_msec = uint64_t(p_interval * 1000);
|
||||
sync_interval_usec = uint64_t(p_interval * 1000 * 1000);
|
||||
}
|
||||
|
||||
double MultiplayerSynchronizer::get_replication_interval() const {
|
||||
return double(interval_msec) / 1000.0;
|
||||
return double(sync_interval_usec) / 1000.0 / 1000.0;
|
||||
}
|
||||
|
||||
void MultiplayerSynchronizer::set_delta_interval(double p_interval) {
|
||||
ERR_FAIL_COND_MSG(p_interval < 0, "Interval must be greater or equal to 0 (where 0 means default)");
|
||||
delta_interval_usec = uint64_t(p_interval * 1000 * 1000);
|
||||
}
|
||||
|
||||
double MultiplayerSynchronizer::get_delta_interval() const {
|
||||
return double(delta_interval_usec) / 1000.0 / 1000.0;
|
||||
}
|
||||
|
||||
void MultiplayerSynchronizer::set_replication_config(Ref<SceneReplicationConfig> p_config) {
|
||||
|
@ -349,6 +364,84 @@ void MultiplayerSynchronizer::set_multiplayer_authority(int p_peer_id, bool p_re
|
|||
get_multiplayer()->object_configuration_add(node, this);
|
||||
}
|
||||
|
||||
Error MultiplayerSynchronizer::_watch_changes(uint64_t p_usec) {
|
||||
ERR_FAIL_COND_V(replication_config.is_null(), FAILED);
|
||||
const List<NodePath> props = replication_config->get_watch_properties();
|
||||
if (props.size() != watchers.size()) {
|
||||
watchers.resize(props.size());
|
||||
}
|
||||
if (props.size() == 0) {
|
||||
return OK;
|
||||
}
|
||||
Node *node = get_root_node();
|
||||
ERR_FAIL_COND_V(!node, FAILED);
|
||||
int idx = -1;
|
||||
Watcher *ptr = watchers.ptrw();
|
||||
for (const NodePath &prop : props) {
|
||||
idx++;
|
||||
bool valid = false;
|
||||
const Object *obj = _get_prop_target(node, prop);
|
||||
ERR_CONTINUE_MSG(!obj, vformat("Node not found for property '%s'.", prop));
|
||||
Variant v = obj->get(prop.get_concatenated_subnames(), &valid);
|
||||
ERR_CONTINUE_MSG(!valid, vformat("Property '%s' not found.", prop));
|
||||
Watcher &w = ptr[idx];
|
||||
if (w.prop != prop) {
|
||||
w.prop = prop;
|
||||
w.value = v.duplicate(true);
|
||||
w.last_change_usec = p_usec;
|
||||
} else if (!w.value.hash_compare(v)) {
|
||||
w.value = v.duplicate(true);
|
||||
w.last_change_usec = p_usec;
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
List<Variant> MultiplayerSynchronizer::get_delta_state(uint64_t p_cur_usec, uint64_t p_last_usec, uint64_t &r_indexes) {
|
||||
r_indexes = 0;
|
||||
List<Variant> out;
|
||||
|
||||
if (last_watch_usec == p_cur_usec) {
|
||||
// We already watched for changes in this frame.
|
||||
|
||||
} else if (p_cur_usec < p_last_usec + delta_interval_usec) {
|
||||
// Too soon skip delta synchronization.
|
||||
return out;
|
||||
|
||||
} else {
|
||||
// Watch for changes.
|
||||
Error err = _watch_changes(p_cur_usec);
|
||||
ERR_FAIL_COND_V(err != OK, out);
|
||||
last_watch_usec = p_cur_usec;
|
||||
}
|
||||
|
||||
const Watcher *ptr = watchers.size() ? watchers.ptr() : nullptr;
|
||||
for (int i = 0; i < watchers.size(); i++) {
|
||||
const Watcher &w = ptr[i];
|
||||
if (w.last_change_usec <= p_last_usec) {
|
||||
continue;
|
||||
}
|
||||
out.push_back(w.value);
|
||||
r_indexes |= 1ULL << i;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
List<NodePath> MultiplayerSynchronizer::get_delta_properties(uint64_t p_indexes) {
|
||||
List<NodePath> out;
|
||||
ERR_FAIL_COND_V(replication_config.is_null(), out);
|
||||
const List<NodePath> watch_props = replication_config->get_watch_properties();
|
||||
int idx = 0;
|
||||
for (const NodePath &prop : watch_props) {
|
||||
if ((p_indexes & (1ULL << idx)) == 0) {
|
||||
continue;
|
||||
}
|
||||
out.push_back(prop);
|
||||
idx++;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
MultiplayerSynchronizer::MultiplayerSynchronizer() {
|
||||
// Publicly visible by default.
|
||||
peer_visibility.insert(0);
|
||||
|
|
|
@ -46,15 +46,24 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
struct Watcher {
|
||||
NodePath prop;
|
||||
uint64_t last_change_usec = 0;
|
||||
Variant value;
|
||||
};
|
||||
|
||||
Ref<SceneReplicationConfig> replication_config;
|
||||
NodePath root_path = NodePath(".."); // Start with parent, like with AnimationPlayer.
|
||||
uint64_t interval_msec = 0;
|
||||
uint64_t sync_interval_usec = 0;
|
||||
uint64_t delta_interval_usec = 0;
|
||||
VisibilityUpdateMode visibility_update_mode = VISIBILITY_PROCESS_IDLE;
|
||||
HashSet<Callable> visibility_filters;
|
||||
HashSet<int> peer_visibility;
|
||||
Vector<Watcher> watchers;
|
||||
uint64_t last_watch_usec = 0;
|
||||
|
||||
ObjectID root_node_cache;
|
||||
uint64_t last_sync_msec = 0;
|
||||
uint64_t last_sync_usec = 0;
|
||||
uint16_t last_inbound_sync = 0;
|
||||
uint32_t net_id = 0;
|
||||
|
||||
|
@ -62,6 +71,7 @@ private:
|
|||
void _start();
|
||||
void _stop();
|
||||
void _update_process();
|
||||
Error _watch_changes(uint64_t p_usec);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
@ -77,7 +87,7 @@ public:
|
|||
uint32_t get_net_id() const;
|
||||
void set_net_id(uint32_t p_net_id);
|
||||
|
||||
bool update_outbound_sync_time(uint64_t p_msec);
|
||||
bool update_outbound_sync_time(uint64_t p_usec);
|
||||
bool update_inbound_sync_time(uint16_t p_network_time);
|
||||
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
|
@ -85,6 +95,9 @@ public:
|
|||
void set_replication_interval(double p_interval);
|
||||
double get_replication_interval() const;
|
||||
|
||||
void set_delta_interval(double p_interval);
|
||||
double get_delta_interval() const;
|
||||
|
||||
void set_replication_config(Ref<SceneReplicationConfig> p_config);
|
||||
Ref<SceneReplicationConfig> get_replication_config();
|
||||
|
||||
|
@ -103,6 +116,9 @@ public:
|
|||
void remove_visibility_filter(Callable p_callback);
|
||||
VisibilityUpdateMode get_visibility_update_mode() const;
|
||||
|
||||
List<Variant> get_delta_state(uint64_t p_cur_usec, uint64_t p_last_usec, uint64_t &r_indexes);
|
||||
List<NodePath> get_delta_properties(uint64_t p_indexes);
|
||||
|
||||
MultiplayerSynchronizer();
|
||||
};
|
||||
|
||||
|
|
|
@ -617,6 +617,22 @@ bool SceneMultiplayer::is_server_relay_enabled() const {
|
|||
return server_relay;
|
||||
}
|
||||
|
||||
void SceneMultiplayer::set_max_sync_packet_size(int p_size) {
|
||||
replicator->set_max_sync_packet_size(p_size);
|
||||
}
|
||||
|
||||
int SceneMultiplayer::get_max_sync_packet_size() const {
|
||||
return replicator->get_max_sync_packet_size();
|
||||
}
|
||||
|
||||
void SceneMultiplayer::set_max_delta_packet_size(int p_size) {
|
||||
replicator->set_max_delta_packet_size(p_size);
|
||||
}
|
||||
|
||||
int SceneMultiplayer::get_max_delta_packet_size() const {
|
||||
return replicator->get_max_delta_packet_size();
|
||||
}
|
||||
|
||||
void SceneMultiplayer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_root_path", "path"), &SceneMultiplayer::set_root_path);
|
||||
ClassDB::bind_method(D_METHOD("get_root_path"), &SceneMultiplayer::get_root_path);
|
||||
|
@ -641,12 +657,19 @@ void SceneMultiplayer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &SceneMultiplayer::is_server_relay_enabled);
|
||||
ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &SceneMultiplayer::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_max_sync_packet_size"), &SceneMultiplayer::get_max_sync_packet_size);
|
||||
ClassDB::bind_method(D_METHOD("set_max_sync_packet_size", "size"), &SceneMultiplayer::set_max_sync_packet_size);
|
||||
ClassDB::bind_method(D_METHOD("get_max_delta_packet_size"), &SceneMultiplayer::get_max_delta_packet_size);
|
||||
ClassDB::bind_method(D_METHOD("set_max_delta_packet_size", "size"), &SceneMultiplayer::set_max_delta_packet_size);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::CALLABLE, "auth_callback"), "set_auth_callback", "get_auth_callback");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auth_timeout", PROPERTY_HINT_RANGE, "0,30,0.1,or_greater,suffix:s"), "set_auth_timeout", "get_auth_timeout");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_sync_packet_size"), "set_max_sync_packet_size", "get_max_sync_packet_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_delta_packet_size"), "set_max_delta_packet_size", "get_max_delta_packet_size");
|
||||
|
||||
ADD_PROPERTY_DEFAULT("refuse_new_connections", false);
|
||||
|
||||
|
|
|
@ -195,6 +195,12 @@ public:
|
|||
void set_server_relay_enabled(bool p_enabled);
|
||||
bool is_server_relay_enabled() const;
|
||||
|
||||
void set_max_sync_packet_size(int p_size);
|
||||
int get_max_sync_packet_size() const;
|
||||
|
||||
void set_max_delta_packet_size(int p_size);
|
||||
int get_max_delta_packet_size() const;
|
||||
|
||||
Ref<SceneCacheInterface> get_path_cache() { return cache; }
|
||||
Ref<SceneReplicationInterface> get_replicator() { return replicator; }
|
||||
|
||||
|
|
|
@ -72,6 +72,14 @@ bool SceneReplicationConfig::_set(const StringName &p_name, const Variant &p_val
|
|||
spawn_props.erase(prop.name);
|
||||
}
|
||||
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);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -94,6 +102,9 @@ bool SceneReplicationConfig::_get(const StringName &p_name, Variant &r_ret) cons
|
|||
} else if (what == "spawn") {
|
||||
r_ret = prop.spawn;
|
||||
return true;
|
||||
} else if (what == "watch") {
|
||||
r_ret = prop.watch;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -104,6 +115,7 @@ void SceneReplicationConfig::_get_property_list(List<PropertyInfo> *p_list) cons
|
|||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,6 +224,27 @@ void SceneReplicationConfig::property_set_sync(const NodePath &p_path, bool p_en
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void SceneReplicationConfig::property_set_watch(const NodePath &p_path, bool p_enabled) {
|
||||
List<ReplicationProperty>::Element *E = properties.find(p_path);
|
||||
ERR_FAIL_COND(!E);
|
||||
if (E->get().watch == p_enabled) {
|
||||
return;
|
||||
}
|
||||
E->get().watch = p_enabled;
|
||||
watch_props.clear();
|
||||
for (const ReplicationProperty &prop : properties) {
|
||||
if (prop.watch) {
|
||||
watch_props.push_back(p_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
@ -222,4 +255,6 @@ void SceneReplicationConfig::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("property_set_spawn", "path", "enabled"), &SceneReplicationConfig::property_set_spawn);
|
||||
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);
|
||||
ClassDB::bind_method(D_METHOD("property_set_watch", "path", "enabled"), &SceneReplicationConfig::property_set_watch);
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ private:
|
|||
NodePath name;
|
||||
bool spawn = true;
|
||||
bool sync = true;
|
||||
bool watch = false;
|
||||
|
||||
bool operator==(const ReplicationProperty &p_to) {
|
||||
return name == p_to.name;
|
||||
|
@ -60,6 +61,7 @@ private:
|
|||
List<ReplicationProperty> properties;
|
||||
List<NodePath> spawn_props;
|
||||
List<NodePath> sync_props;
|
||||
List<NodePath> watch_props;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
@ -82,8 +84,12 @@ public:
|
|||
bool property_get_sync(const NodePath &p_path);
|
||||
void property_set_sync(const NodePath &p_path, bool p_enabled);
|
||||
|
||||
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; }
|
||||
|
||||
SceneReplicationConfig() {}
|
||||
};
|
||||
|
|
|
@ -138,15 +138,16 @@ void SceneReplicationInterface::on_network_process() {
|
|||
spawn_queue.clear();
|
||||
}
|
||||
|
||||
// Process timed syncs.
|
||||
uint64_t msec = OS::get_singleton()->get_ticks_msec();
|
||||
// Process syncs.
|
||||
uint64_t usec = OS::get_singleton()->get_ticks_usec();
|
||||
for (KeyValue<int, PeerInfo> &E : peers_info) {
|
||||
const HashSet<ObjectID> to_sync = E.value.sync_nodes;
|
||||
if (to_sync.is_empty()) {
|
||||
continue; // Nothing to sync
|
||||
}
|
||||
uint16_t sync_net_time = ++E.value.last_sent_sync;
|
||||
_send_sync(E.key, to_sync, sync_net_time, msec);
|
||||
_send_sync(E.key, to_sync, sync_net_time, usec);
|
||||
_send_delta(E.key, to_sync, usec, E.value.last_watch_usecs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,6 +281,7 @@ Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_co
|
|||
sync_nodes.erase(sid);
|
||||
for (KeyValue<int, PeerInfo> &E : peers_info) {
|
||||
E.value.sync_nodes.erase(sid);
|
||||
E.value.last_watch_usecs.erase(sid);
|
||||
if (sync->get_net_id()) {
|
||||
E.value.recv_sync_ids.erase(sync->get_net_id());
|
||||
}
|
||||
|
@ -357,6 +359,7 @@ Error SceneReplicationInterface::_update_sync_visibility(int p_peer, Multiplayer
|
|||
E.value.sync_nodes.insert(sid);
|
||||
} else {
|
||||
E.value.sync_nodes.erase(sid);
|
||||
E.value.last_watch_usecs.erase(sid);
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
|
@ -369,6 +372,7 @@ Error SceneReplicationInterface::_update_sync_visibility(int p_peer, Multiplayer
|
|||
peers_info[p_peer].sync_nodes.insert(sid);
|
||||
} else {
|
||||
peers_info[p_peer].sync_nodes.erase(sid);
|
||||
peers_info[p_peer].last_watch_usecs.erase(sid);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
@ -670,8 +674,126 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p
|
|||
return OK;
|
||||
}
|
||||
|
||||
void SceneReplicationInterface::_send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_msec) {
|
||||
MAKE_ROOM(sync_mtu);
|
||||
bool SceneReplicationInterface::_verify_synchronizer(int p_peer, MultiplayerSynchronizer *p_sync, uint32_t &r_net_id) {
|
||||
r_net_id = p_sync->get_net_id();
|
||||
if (r_net_id == 0 || (r_net_id & 0x80000000)) {
|
||||
int path_id = 0;
|
||||
bool verified = multiplayer->get_path_cache()->send_object_cache(p_sync, p_peer, path_id);
|
||||
ERR_FAIL_COND_V_MSG(path_id < 0, false, "This should never happen!");
|
||||
if (r_net_id == 0) {
|
||||
// First time path based ID.
|
||||
r_net_id = path_id | 0x80000000;
|
||||
p_sync->set_net_id(r_net_id | 0x80000000);
|
||||
}
|
||||
return verified;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MultiplayerSynchronizer *SceneReplicationInterface::_find_synchronizer(int p_peer, uint32_t p_net_id) {
|
||||
MultiplayerSynchronizer *sync = nullptr;
|
||||
if (p_net_id & 0x80000000) {
|
||||
sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_path_cache()->get_cached_object(p_peer, p_net_id & 0x7FFFFFFF));
|
||||
} else if (peers_info[p_peer].recv_sync_ids.has(p_net_id)) {
|
||||
const ObjectID &sid = peers_info[p_peer].recv_sync_ids[p_net_id];
|
||||
sync = get_id_as<MultiplayerSynchronizer>(sid);
|
||||
}
|
||||
return sync;
|
||||
}
|
||||
|
||||
void SceneReplicationInterface::_send_delta(int p_peer, const HashSet<ObjectID> p_synchronizers, uint64_t p_usec, const HashMap<ObjectID, uint64_t> p_last_watch_usecs) {
|
||||
MAKE_ROOM(/* header */ 1 + /* element */ 4 + 8 + 4 + delta_mtu);
|
||||
uint8_t *ptr = packet_cache.ptrw();
|
||||
ptr[0] = SceneMultiplayer::NETWORK_COMMAND_SYNC | (1 << SceneMultiplayer::CMD_FLAG_0_SHIFT);
|
||||
int ofs = 1;
|
||||
for (const ObjectID &oid : p_synchronizers) {
|
||||
MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(oid);
|
||||
ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid() || !sync->is_multiplayer_authority());
|
||||
uint32_t net_id;
|
||||
if (!_verify_synchronizer(p_peer, sync, net_id)) {
|
||||
continue;
|
||||
}
|
||||
uint64_t last_usec = p_last_watch_usecs.has(oid) ? p_last_watch_usecs[oid] : 0;
|
||||
uint64_t indexes;
|
||||
List<Variant> delta = sync->get_delta_state(p_usec, last_usec, indexes);
|
||||
|
||||
if (!delta.size()) {
|
||||
continue; // Nothing to update.
|
||||
}
|
||||
|
||||
Vector<const Variant *> varp;
|
||||
varp.resize(delta.size());
|
||||
const Variant **vptr = varp.ptrw();
|
||||
int i = 0;
|
||||
for (const Variant &v : delta) {
|
||||
vptr[i] = &v;
|
||||
}
|
||||
int size;
|
||||
Error err = MultiplayerAPI::encode_and_compress_variants(vptr, varp.size(), nullptr, size);
|
||||
ERR_CONTINUE_MSG(err != OK, "Unable to encode delta state.");
|
||||
|
||||
ERR_CONTINUE_MSG(size > delta_mtu, vformat("Synchronizer delta bigger than MTU will not be sent (%d > %d): %s", size, delta_mtu, sync->get_path()));
|
||||
|
||||
if (ofs + 4 + 8 + 4 + size > delta_mtu) {
|
||||
// Send what we got, and reset write.
|
||||
_send_raw(packet_cache.ptr(), ofs, p_peer, true);
|
||||
ofs = 1;
|
||||
}
|
||||
if (size) {
|
||||
ofs += encode_uint32(sync->get_net_id(), &ptr[ofs]);
|
||||
ofs += encode_uint64(indexes, &ptr[ofs]);
|
||||
ofs += encode_uint32(size, &ptr[ofs]);
|
||||
MultiplayerAPI::encode_and_compress_variants(vptr, varp.size(), &ptr[ofs], size);
|
||||
ofs += size;
|
||||
}
|
||||
#ifdef DEBUG_ENABLED
|
||||
_profile_node_data("delta_out", oid, size);
|
||||
#endif
|
||||
peers_info[p_peer].last_watch_usecs[oid] = p_usec;
|
||||
}
|
||||
if (ofs > 1) {
|
||||
// Got some left over to send.
|
||||
_send_raw(packet_cache.ptr(), ofs, p_peer, true);
|
||||
}
|
||||
}
|
||||
|
||||
Error SceneReplicationInterface::on_delta_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
|
||||
int ofs = 1;
|
||||
while (ofs + 4 + 8 + 4 < p_buffer_len) {
|
||||
uint32_t net_id = decode_uint32(&p_buffer[ofs]);
|
||||
ofs += 4;
|
||||
uint64_t indexes = decode_uint64(&p_buffer[ofs]);
|
||||
ofs += 8;
|
||||
uint32_t size = decode_uint32(&p_buffer[ofs]);
|
||||
ofs += 4;
|
||||
ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA);
|
||||
MultiplayerSynchronizer *sync = _find_synchronizer(p_from, net_id);
|
||||
Node *node = sync ? sync->get_root_node() : nullptr;
|
||||
if (!sync || sync->get_multiplayer_authority() != p_from || !node) {
|
||||
ofs += size;
|
||||
ERR_CONTINUE_MSG(true, "Ignoring delta for non-authority or invalid synchronizer.");
|
||||
}
|
||||
List<NodePath> props = sync->get_delta_properties(indexes);
|
||||
ERR_FAIL_COND_V(props.size() == 0, ERR_INVALID_DATA);
|
||||
Vector<Variant> vars;
|
||||
vars.resize(props.size());
|
||||
int consumed = 0;
|
||||
Error err = MultiplayerAPI::decode_and_decompress_variants(vars, p_buffer + ofs, size, consumed);
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
ERR_FAIL_COND_V(uint32_t(consumed) != size, ERR_INVALID_DATA);
|
||||
err = MultiplayerSynchronizer::set_state(props, node, vars);
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
ofs += size;
|
||||
sync->emit_signal(SNAME("delta_synchronized"));
|
||||
#ifdef DEBUG_ENABLED
|
||||
_profile_node_data("delta_in", sync->get_instance_id(), size);
|
||||
#endif
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void SceneReplicationInterface::_send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_usec) {
|
||||
MAKE_ROOM(/* header */ 3 + /* element */ 4 + 4 + sync_mtu);
|
||||
uint8_t *ptr = packet_cache.ptrw();
|
||||
ptr[0] = SceneMultiplayer::NETWORK_COMMAND_SYNC;
|
||||
int ofs = 1;
|
||||
|
@ -681,26 +803,16 @@ void SceneReplicationInterface::_send_sync(int p_peer, const HashSet<ObjectID> p
|
|||
for (const ObjectID &oid : p_synchronizers) {
|
||||
MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(oid);
|
||||
ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid() || !sync->is_multiplayer_authority());
|
||||
if (!sync->update_outbound_sync_time(p_msec)) {
|
||||
if (!sync->update_outbound_sync_time(p_usec)) {
|
||||
continue; // nothing to sync.
|
||||
}
|
||||
|
||||
Node *node = sync->get_root_node();
|
||||
ERR_CONTINUE(!node);
|
||||
uint32_t net_id = sync->get_net_id();
|
||||
if (net_id == 0 || (net_id & 0x80000000)) {
|
||||
int path_id = 0;
|
||||
bool verified = multiplayer->get_path_cache()->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;
|
||||
sync->set_net_id(net_id | 0x80000000);
|
||||
}
|
||||
if (!verified) {
|
||||
// The path based sync is not yet confirmed, skipping.
|
||||
continue;
|
||||
}
|
||||
if (!_verify_synchronizer(p_peer, sync, net_id)) {
|
||||
// The path based sync is not yet confirmed, skipping.
|
||||
continue;
|
||||
}
|
||||
int size;
|
||||
Vector<Variant> vars;
|
||||
|
@ -711,7 +823,7 @@ void SceneReplicationInterface::_send_sync(int p_peer, const HashSet<ObjectID> p
|
|||
err = MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), nullptr, size);
|
||||
ERR_CONTINUE_MSG(err != OK, "Unable to encode sync state.");
|
||||
// TODO Handle single state above MTU.
|
||||
ERR_CONTINUE_MSG(size > 3 + 4 + 4 + sync_mtu, vformat("Node states bigger then MTU will not be sent (%d > %d): %s", size, sync_mtu, node->get_path()));
|
||||
ERR_CONTINUE_MSG(size > sync_mtu, vformat("Node states bigger than MTU will not be sent (%d > %d): %s", size, sync_mtu, node->get_path()));
|
||||
if (ofs + 4 + 4 + size > sync_mtu) {
|
||||
// Send what we got, and reset write.
|
||||
_send_raw(packet_cache.ptr(), ofs, p_peer, false);
|
||||
|
@ -735,6 +847,10 @@ void SceneReplicationInterface::_send_sync(int p_peer, const HashSet<ObjectID> p
|
|||
|
||||
Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
|
||||
ERR_FAIL_COND_V_MSG(p_buffer_len < 11, ERR_INVALID_DATA, "Invalid sync packet received");
|
||||
bool is_delta = (p_buffer[0] & (1 << SceneMultiplayer::CMD_FLAG_0_SHIFT)) != 0;
|
||||
if (is_delta) {
|
||||
return on_delta_receive(p_from, p_buffer, p_buffer_len);
|
||||
}
|
||||
uint16_t time = decode_uint16(&p_buffer[1]);
|
||||
int ofs = 3;
|
||||
while (ofs + 8 < p_buffer_len) {
|
||||
|
@ -743,13 +859,7 @@ Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_bu
|
|||
uint32_t size = decode_uint32(&p_buffer[ofs]);
|
||||
ofs += 4;
|
||||
ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA);
|
||||
MultiplayerSynchronizer *sync = nullptr;
|
||||
if (net_id & 0x80000000) {
|
||||
sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_path_cache()->get_cached_object(p_from, net_id & 0x7FFFFFFF));
|
||||
} else if (peers_info[p_from].recv_sync_ids.has(net_id)) {
|
||||
const ObjectID &sid = peers_info[p_from].recv_sync_ids[net_id];
|
||||
sync = get_id_as<MultiplayerSynchronizer>(sid);
|
||||
}
|
||||
MultiplayerSynchronizer *sync = _find_synchronizer(p_from, net_id);
|
||||
if (!sync) {
|
||||
// Not received yet.
|
||||
ofs += size;
|
||||
|
@ -782,3 +892,21 @@ Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_bu
|
|||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void SceneReplicationInterface::set_max_sync_packet_size(int p_size) {
|
||||
ERR_FAIL_COND_MSG(p_size < 128, "Sync maximum packet size must be at least 128 bytes.");
|
||||
sync_mtu = p_size;
|
||||
}
|
||||
|
||||
int SceneReplicationInterface::get_max_sync_packet_size() const {
|
||||
return sync_mtu;
|
||||
}
|
||||
|
||||
void SceneReplicationInterface::set_max_delta_packet_size(int p_size) {
|
||||
ERR_FAIL_COND_MSG(p_size < 128, "Sync maximum packet size must be at least 128 bytes.");
|
||||
delta_mtu = p_size;
|
||||
}
|
||||
|
||||
int SceneReplicationInterface::get_max_delta_packet_size() const {
|
||||
return delta_mtu;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ private:
|
|||
struct PeerInfo {
|
||||
HashSet<ObjectID> sync_nodes;
|
||||
HashSet<ObjectID> spawn_nodes;
|
||||
HashMap<ObjectID, uint64_t> last_watch_usecs;
|
||||
HashMap<uint32_t, ObjectID> recv_sync_ids;
|
||||
HashMap<uint32_t, ObjectID> recv_nodes;
|
||||
uint16_t last_sent_sync = 0;
|
||||
|
@ -88,12 +89,17 @@ private:
|
|||
SceneMultiplayer *multiplayer = nullptr;
|
||||
PackedByteArray packet_cache;
|
||||
int sync_mtu = 1350; // Highly dependent on underlying protocol.
|
||||
int delta_mtu = 65535;
|
||||
|
||||
TrackedNode &_track(const ObjectID &p_id);
|
||||
void _untrack(const ObjectID &p_id);
|
||||
void _node_ready(const ObjectID &p_oid);
|
||||
|
||||
void _send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_msec);
|
||||
bool _verify_synchronizer(int p_peer, MultiplayerSynchronizer *p_sync, uint32_t &r_net_id);
|
||||
MultiplayerSynchronizer *_find_synchronizer(int p_peer, uint32_t p_net_ida);
|
||||
|
||||
void _send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_usec);
|
||||
void _send_delta(int p_peer, const HashSet<ObjectID> p_synchronizers, uint64_t p_usec, const HashMap<ObjectID, uint64_t> p_last_watch_usecs);
|
||||
Error _make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, 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);
|
||||
|
@ -127,9 +133,16 @@ public:
|
|||
Error on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len);
|
||||
Error on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len);
|
||||
Error on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len);
|
||||
Error on_delta_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len);
|
||||
|
||||
bool is_rpc_visible(const ObjectID &p_oid, int p_peer) const;
|
||||
|
||||
void set_max_sync_packet_size(int p_size);
|
||||
int get_max_sync_packet_size() const;
|
||||
|
||||
void set_max_delta_packet_size(int p_size);
|
||||
int get_max_delta_packet_size() const;
|
||||
|
||||
SceneReplicationInterface(SceneMultiplayer *p_multiplayer) {
|
||||
multiplayer = p_multiplayer;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue