2021-10-08 14:13:06 +02:00
/**************************************************************************/
/* multiplayer_synchronizer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
# include "multiplayer_synchronizer.h"
# include "core/config/engine.h"
2022-07-12 23:12:42 +02:00
# include "scene/main/multiplayer_api.h"
2021-10-08 14:13:06 +02:00
Object * MultiplayerSynchronizer : : _get_prop_target ( Object * p_obj , const NodePath & p_path ) {
if ( p_path . get_name_count ( ) = = 0 ) {
return p_obj ;
}
Node * node = Object : : cast_to < Node > ( p_obj ) ;
ERR_FAIL_COND_V_MSG ( ! node | | ! node - > has_node ( p_path ) , nullptr , vformat ( " Node '%s' not found. " , p_path ) ) ;
return node - > get_node ( p_path ) ;
}
void MultiplayerSynchronizer : : _stop ( ) {
2022-07-09 20:45:30 +02:00
# ifdef TOOLS_ENABLED
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
return ;
}
# endif
2022-09-30 22:35:56 +02:00
root_node_cache = ObjectID ( ) ;
2021-10-08 14:13:06 +02:00
Node * node = is_inside_tree ( ) ? get_node_or_null ( root_path ) : nullptr ;
if ( node ) {
2022-07-12 23:12:42 +02:00
get_multiplayer ( ) - > object_configuration_remove ( node , this ) ;
2021-10-08 14:13:06 +02:00
}
2024-01-14 20:13:35 +01:00
reset ( ) ;
2021-10-08 14:13:06 +02:00
}
void MultiplayerSynchronizer : : _start ( ) {
2022-07-09 20:45:30 +02:00
# ifdef TOOLS_ENABLED
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
return ;
}
# endif
2022-09-30 22:35:56 +02:00
root_node_cache = ObjectID ( ) ;
reset ( ) ;
2021-10-08 14:13:06 +02:00
Node * node = is_inside_tree ( ) ? get_node_or_null ( root_path ) : nullptr ;
if ( node ) {
2022-09-30 22:35:56 +02:00
root_node_cache = node - > get_instance_id ( ) ;
2022-07-12 23:12:42 +02:00
get_multiplayer ( ) - > object_configuration_add ( node , this ) ;
2022-07-09 20:45:30 +02:00
_update_process ( ) ;
}
}
void MultiplayerSynchronizer : : _update_process ( ) {
# ifdef TOOLS_ENABLED
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
return ;
}
# endif
Node * node = is_inside_tree ( ) ? get_node_or_null ( root_path ) : nullptr ;
if ( ! node ) {
return ;
}
set_process_internal ( false ) ;
set_physics_process_internal ( false ) ;
if ( ! visibility_filters . size ( ) ) {
return ;
}
switch ( visibility_update_mode ) {
case VISIBILITY_PROCESS_IDLE :
set_process_internal ( true ) ;
break ;
case VISIBILITY_PROCESS_PHYSICS :
set_physics_process_internal ( true ) ;
break ;
case VISIBILITY_PROCESS_NONE :
break ;
2021-10-08 14:13:06 +02:00
}
}
2022-09-30 22:35:56 +02:00
Node * MultiplayerSynchronizer : : get_root_node ( ) {
return root_node_cache . is_valid ( ) ? Object : : cast_to < Node > ( ObjectDB : : get_instance ( root_node_cache ) ) : nullptr ;
}
void MultiplayerSynchronizer : : reset ( ) {
net_id = 0 ;
2023-03-28 09:30:58 +02:00
last_sync_usec = 0 ;
2022-09-30 22:35:56 +02:00
last_inbound_sync = 0 ;
2023-10-11 18:55:32 +02:00
last_watch_usec = 0 ;
sync_started = false ;
watchers . clear ( ) ;
2022-09-30 22:35:56 +02:00
}
uint32_t MultiplayerSynchronizer : : get_net_id ( ) const {
return net_id ;
}
void MultiplayerSynchronizer : : set_net_id ( uint32_t p_net_id ) {
net_id = p_net_id ;
}
2023-03-28 09:30:58 +02:00
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.
2022-10-23 08:01:51 +02:00
return true ;
}
2023-03-28 09:30:58 +02:00
if ( p_usec < last_sync_usec + sync_interval_usec ) {
// Too soon, should skip this synchronization frame.
return false ;
2022-09-30 22:35:56 +02:00
}
2023-03-28 09:30:58 +02:00
last_sync_usec = p_usec ;
return true ;
2022-09-30 22:35:56 +02:00
}
bool MultiplayerSynchronizer : : update_inbound_sync_time ( uint16_t p_network_time ) {
2023-10-11 18:55:32 +02:00
if ( ! sync_started ) {
sync_started = true ;
} else if ( p_network_time < = last_inbound_sync & & last_inbound_sync - p_network_time < 32767 ) {
2022-09-30 22:35:56 +02:00
return false ;
}
last_inbound_sync = p_network_time ;
return true ;
}
2024-02-17 19:03:21 +01:00
PackedStringArray MultiplayerSynchronizer : : get_configuration_warnings ( ) const {
PackedStringArray warnings = Node : : get_configuration_warnings ( ) ;
2022-09-16 22:42:05 +02:00
if ( root_path . is_empty ( ) | | ! has_node ( root_path ) ) {
warnings . push_back ( RTR ( " A valid NodePath must be set in the \" Root Path \" property in order for MultiplayerSynchronizer to be able to synchronize properties. " ) ) ;
}
return warnings ;
}
2021-10-08 14:13:06 +02:00
Error MultiplayerSynchronizer : : get_state ( const List < NodePath > & p_properties , Object * p_obj , Vector < Variant > & r_variant , Vector < const Variant * > & r_variant_ptrs ) {
2023-09-09 17:40:07 +02:00
ERR_FAIL_NULL_V ( p_obj , ERR_INVALID_PARAMETER ) ;
2021-10-08 14:13:06 +02:00
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 ) ;
2023-09-09 17:40:07 +02:00
ERR_FAIL_NULL_V ( obj , FAILED ) ;
2023-07-14 20:37:26 +02:00
r_variant . write [ i ] = obj - > get_indexed ( prop . get_subnames ( ) , & valid ) ;
2021-10-08 14:13:06 +02:00
r_variant_ptrs . write [ i ] = & r_variant [ i ] ;
ERR_FAIL_COND_V_MSG ( ! valid , ERR_INVALID_DATA , vformat ( " Property '%s' not found. " , prop ) ) ;
i + + ;
}
return OK ;
}
Error MultiplayerSynchronizer : : set_state ( const List < NodePath > & p_properties , Object * p_obj , const Vector < Variant > & p_state ) {
2023-09-09 17:40:07 +02:00
ERR_FAIL_NULL_V ( p_obj , ERR_INVALID_PARAMETER ) ;
2021-10-08 14:13:06 +02:00
int i = 0 ;
for ( const NodePath & prop : p_properties ) {
Object * obj = _get_prop_target ( p_obj , prop ) ;
2023-09-09 17:40:07 +02:00
ERR_FAIL_NULL_V ( obj , FAILED ) ;
2023-07-14 20:37:26 +02:00
obj - > set_indexed ( prop . get_subnames ( ) , p_state [ i ] ) ;
2021-10-08 14:13:06 +02:00
i + = 1 ;
}
return OK ;
}
2022-07-09 20:45:30 +02:00
bool MultiplayerSynchronizer : : is_visibility_public ( ) const {
return peer_visibility . has ( 0 ) ;
}
void MultiplayerSynchronizer : : set_visibility_public ( bool p_visible ) {
set_visibility_for ( 0 , p_visible ) ;
}
bool MultiplayerSynchronizer : : is_visible_to ( int p_peer ) {
if ( visibility_filters . size ( ) ) {
Variant arg = p_peer ;
const Variant * argv [ 1 ] = { & arg } ;
for ( Callable filter : visibility_filters ) {
Variant ret ;
Callable : : CallError err ;
2022-07-28 22:56:41 +02:00
filter . callp ( argv , 1 , ret , err ) ;
2022-07-09 20:45:30 +02:00
ERR_FAIL_COND_V ( err . error ! = Callable : : CallError : : CALL_OK | | ret . get_type ( ) ! = Variant : : BOOL , false ) ;
if ( ! ret . operator bool ( ) ) {
return false ;
}
}
}
return peer_visibility . has ( 0 ) | | peer_visibility . has ( p_peer ) ;
}
void MultiplayerSynchronizer : : add_visibility_filter ( Callable p_callback ) {
visibility_filters . insert ( p_callback ) ;
_update_process ( ) ;
}
void MultiplayerSynchronizer : : remove_visibility_filter ( Callable p_callback ) {
visibility_filters . erase ( p_callback ) ;
_update_process ( ) ;
}
void MultiplayerSynchronizer : : set_visibility_for ( int p_peer , bool p_visible ) {
if ( peer_visibility . has ( p_peer ) = = p_visible ) {
return ;
}
if ( p_visible ) {
peer_visibility . insert ( p_peer ) ;
} else {
peer_visibility . erase ( p_peer ) ;
}
update_visibility ( p_peer ) ;
}
bool MultiplayerSynchronizer : : get_visibility_for ( int p_peer ) const {
return peer_visibility . has ( p_peer ) ;
}
void MultiplayerSynchronizer : : set_visibility_update_mode ( VisibilityUpdateMode p_mode ) {
visibility_update_mode = p_mode ;
_update_process ( ) ;
}
MultiplayerSynchronizer : : VisibilityUpdateMode MultiplayerSynchronizer : : get_visibility_update_mode ( ) const {
return visibility_update_mode ;
}
2021-10-08 14:13:06 +02:00
void MultiplayerSynchronizer : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " set_root_path " , " path " ) , & MultiplayerSynchronizer : : set_root_path ) ;
ClassDB : : bind_method ( D_METHOD ( " get_root_path " ) , & MultiplayerSynchronizer : : get_root_path ) ;
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 ) ;
2023-03-28 09:30:58 +02:00
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 ) ;
2021-10-08 14:13:06 +02:00
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 ) ;
2022-05-20 07:24:41 +02:00
2022-07-09 20:45:30 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_visibility_update_mode " , " mode " ) , & MultiplayerSynchronizer : : set_visibility_update_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_visibility_update_mode " ) , & MultiplayerSynchronizer : : get_visibility_update_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " update_visibility " , " for_peer " ) , & MultiplayerSynchronizer : : update_visibility , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " set_visibility_public " , " visible " ) , & MultiplayerSynchronizer : : set_visibility_public ) ;
ClassDB : : bind_method ( D_METHOD ( " is_visibility_public " ) , & MultiplayerSynchronizer : : is_visibility_public ) ;
ClassDB : : bind_method ( D_METHOD ( " add_visibility_filter " , " filter " ) , & MultiplayerSynchronizer : : add_visibility_filter ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_visibility_filter " , " filter " ) , & MultiplayerSynchronizer : : remove_visibility_filter ) ;
ClassDB : : bind_method ( D_METHOD ( " set_visibility_for " , " peer " , " visible " ) , & MultiplayerSynchronizer : : set_visibility_for ) ;
ClassDB : : bind_method ( D_METHOD ( " get_visibility_for " , " peer " ) , & MultiplayerSynchronizer : : get_visibility_for ) ;
2022-05-20 07:24:41 +02:00
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 " ) ;
2023-03-28 09:30:58 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " delta_interval " , PROPERTY_HINT_RANGE , " 0,5,0.001,suffix:s " ) , " set_delta_interval " , " get_delta_interval " ) ;
2022-07-09 20:45:30 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " replication_config " , PROPERTY_HINT_RESOURCE_TYPE , " SceneReplicationConfig " , PROPERTY_USAGE_NO_EDITOR ) , " set_replication_config " , " get_replication_config " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " visibility_update_mode " , PROPERTY_HINT_ENUM , " Idle,Physics,None " ) , " set_visibility_update_mode " , " get_visibility_update_mode " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " public_visibility " ) , " set_visibility_public " , " is_visibility_public " ) ;
BIND_ENUM_CONSTANT ( VISIBILITY_PROCESS_IDLE ) ;
BIND_ENUM_CONSTANT ( VISIBILITY_PROCESS_PHYSICS ) ;
BIND_ENUM_CONSTANT ( VISIBILITY_PROCESS_NONE ) ;
2023-02-20 14:46:39 +01:00
ADD_SIGNAL ( MethodInfo ( " synchronized " ) ) ;
2023-03-28 09:30:58 +02:00
ADD_SIGNAL ( MethodInfo ( " delta_synchronized " ) ) ;
2022-07-09 20:45:30 +02:00
ADD_SIGNAL ( MethodInfo ( " visibility_changed " , PropertyInfo ( Variant : : INT , " for_peer " ) ) ) ;
2021-10-08 14:13:06 +02:00
}
void MultiplayerSynchronizer : : _notification ( int p_what ) {
# ifdef TOOLS_ENABLED
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
return ;
}
# endif
if ( root_path . is_empty ( ) ) {
return ;
}
2022-02-15 18:06:48 +01:00
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE : {
_start ( ) ;
} break ;
case NOTIFICATION_EXIT_TREE : {
_stop ( ) ;
} break ;
2022-07-09 20:45:30 +02:00
case NOTIFICATION_INTERNAL_PROCESS :
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS : {
update_visibility ( 0 ) ;
} break ;
2021-10-08 14:13:06 +02:00
}
}
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) " ) ;
2023-03-28 09:30:58 +02:00
sync_interval_usec = uint64_t ( p_interval * 1000 * 1000 ) ;
2021-10-08 14:13:06 +02:00
}
double MultiplayerSynchronizer : : get_replication_interval ( ) const {
2023-03-28 09:30:58 +02:00
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 ;
2021-10-08 14:13:06 +02:00
}
void MultiplayerSynchronizer : : set_replication_config ( Ref < SceneReplicationConfig > p_config ) {
replication_config = p_config ;
}
Ref < SceneReplicationConfig > MultiplayerSynchronizer : : get_replication_config ( ) {
return replication_config ;
}
2022-07-09 20:45:30 +02:00
void MultiplayerSynchronizer : : update_visibility ( int p_for_peer ) {
# ifdef TOOLS_ENABLED
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
return ;
}
# endif
Node * node = is_inside_tree ( ) ? get_node_or_null ( root_path ) : nullptr ;
if ( node & & get_multiplayer ( ) - > has_multiplayer_peer ( ) & & is_multiplayer_authority ( ) ) {
emit_signal ( SNAME ( " visibility_changed " ) , p_for_peer ) ;
}
}
2021-10-08 14:13:06 +02:00
void MultiplayerSynchronizer : : set_root_path ( const NodePath & p_path ) {
2023-10-11 18:55:32 +02:00
if ( p_path = = root_path ) {
return ;
}
2021-10-08 14:13:06 +02:00
_stop ( ) ;
root_path = p_path ;
_start ( ) ;
}
NodePath MultiplayerSynchronizer : : get_root_path ( ) const {
return root_path ;
}
void MultiplayerSynchronizer : : set_multiplayer_authority ( int p_peer_id , bool p_recursive ) {
2023-10-11 18:55:32 +02:00
if ( get_multiplayer_authority ( ) = = p_peer_id ) {
2021-10-08 14:13:06 +02:00
return ;
}
2023-10-11 18:55:32 +02:00
_stop ( ) ;
2021-10-08 14:13:06 +02:00
Node : : set_multiplayer_authority ( p_peer_id , p_recursive ) ;
2023-10-11 18:55:32 +02:00
_start ( ) ;
2021-10-08 14:13:06 +02:00
}
2022-07-09 20:45:30 +02:00
2023-03-28 09:30:58 +02:00
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 ( ) ;
2023-09-09 17:40:07 +02:00
ERR_FAIL_NULL_V ( node , FAILED ) ;
2023-03-28 09:30:58 +02:00
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 ) ) ;
2023-10-13 16:43:30 +02:00
Variant v = obj - > get_indexed ( prop . get_subnames ( ) , & valid ) ;
2023-03-28 09:30:58 +02:00
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 ) {
2023-06-26 12:33:30 +02:00
if ( ( p_indexes & ( 1ULL < < idx + + ) ) = = 0 ) {
2023-03-28 09:30:58 +02:00
continue ;
}
out . push_back ( prop ) ;
}
return out ;
}
2023-10-02 10:56:21 +02:00
SceneReplicationConfig * MultiplayerSynchronizer : : get_replication_config_ptr ( ) const {
return replication_config . ptr ( ) ;
}
2022-07-09 20:45:30 +02:00
MultiplayerSynchronizer : : MultiplayerSynchronizer ( ) {
// Publicly visible by default.
peer_visibility . insert ( 0 ) ;
}