2021-08-09 22:46:23 +02:00
/*************************************************************************/
/* multiplayer_replicator.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
2021-09-03 19:40:47 +02:00
# include "core/multiplayer/multiplayer_replicator.h"
2021-08-09 22:46:23 +02:00
# include "core/io/marshalls.h"
# include "scene/main/node.h"
# include "scene/resources/packed_scene.h"
# define MAKE_ROOM(m_amount) \
if ( packet_cache . size ( ) < m_amount ) \
packet_cache . resize ( m_amount ) ;
2021-08-12 21:07:44 +02:00
Error MultiplayerReplicator : : _sync_all_default ( const ResourceUID : : ID & p_scene_id , int p_peer ) {
ERR_FAIL_COND_V ( ! replications . has ( p_scene_id ) , ERR_INVALID_PARAMETER ) ;
SceneConfig & cfg = replications [ p_scene_id ] ;
int full_size = 0 ;
bool same_size = true ;
int last_size = 0 ;
bool all_raw = true ;
struct EncodeInfo {
int size = 0 ;
bool raw = false ;
List < Variant > state ;
} ;
Map < ObjectID , struct EncodeInfo > state ;
if ( tracked_objects . has ( p_scene_id ) ) {
for ( const ObjectID & obj_id : tracked_objects [ p_scene_id ] ) {
Object * obj = ObjectDB : : get_instance ( obj_id ) ;
if ( obj ) {
struct EncodeInfo info ;
Error err = _get_state ( cfg . sync_properties , obj , info . state ) ;
ERR_CONTINUE ( err ) ;
err = _encode_state ( info . state , nullptr , info . size , & info . raw ) ;
ERR_CONTINUE ( err ) ;
state [ obj_id ] = info ;
full_size + = info . size ;
if ( last_size & & info . size ! = last_size ) {
same_size = false ;
}
all_raw = all_raw & & info . raw ;
last_size = info . size ;
}
}
}
// Default implementation do not send empty updates.
if ( ! full_size ) {
return OK ;
}
# ifdef DEBUG_ENABLED
if ( full_size > 4096 & & cfg . sync_interval ) {
WARN_PRINT_ONCE ( vformat ( " The timed state update for scene %d is big (%d bytes) consider optimizing it " , p_scene_id ) ) ;
}
# endif
if ( same_size ) {
// This is fast and small. Should we allow more than 256 objects per type?
// This costs us 1 byte.
MAKE_ROOM ( SYNC_CMD_OFFSET + 1 + 2 + 2 + full_size ) ;
} else {
MAKE_ROOM ( SYNC_CMD_OFFSET + 1 + 2 + state . size ( ) * 2 + full_size ) ;
}
int ofs = 0 ;
uint8_t * ptr = packet_cache . ptrw ( ) ;
2021-09-03 19:40:47 +02:00
ptr [ 0 ] = MultiplayerAPI : : NETWORK_COMMAND_SYNC | ( same_size ? BYTE_OR_ZERO_FLAG : 0 ) ;
2021-08-12 21:07:44 +02:00
ofs = 1 ;
ofs + = encode_uint64 ( p_scene_id , & ptr [ ofs ] ) ;
ptr [ ofs ] = cfg . sync_recv + + ;
ofs + = 1 ;
ofs + = encode_uint16 ( state . size ( ) , & ptr [ ofs ] ) ;
if ( same_size ) {
ofs + = encode_uint16 ( last_size + ( all_raw ? 1 < < 15 : 0 ) , & ptr [ ofs ] ) ;
}
for ( const ObjectID & obj_id : tracked_objects [ p_scene_id ] ) {
if ( ! state . has ( obj_id ) ) {
continue ;
}
struct EncodeInfo & info = state [ obj_id ] ;
Object * obj = ObjectDB : : get_instance ( obj_id ) ;
ERR_CONTINUE ( ! obj ) ;
int size = 0 ;
if ( ! same_size ) {
// We need to encode the size of every object.
ofs + = encode_uint16 ( info . size + ( info . raw ? 1 < < 15 : 0 ) , & ptr [ ofs ] ) ;
}
Error err = _encode_state ( info . state , & ptr [ ofs ] , size , & info . raw ) ;
ERR_CONTINUE ( err ) ;
ofs + = size ;
}
Ref < MultiplayerPeer > network_peer = multiplayer - > get_network_peer ( ) ;
network_peer - > set_target_peer ( p_peer ) ;
network_peer - > set_transfer_channel ( 0 ) ;
2021-09-03 19:40:47 +02:00
network_peer - > set_transfer_mode ( Multiplayer : : TRANSFER_MODE_UNRELIABLE ) ;
2021-08-12 21:07:44 +02:00
return network_peer - > put_packet ( ptr , ofs ) ;
}
void MultiplayerReplicator : : _process_default_sync ( const ResourceUID : : ID & p_id , const uint8_t * p_packet , int p_packet_len ) {
ERR_FAIL_COND_MSG ( p_packet_len < SYNC_CMD_OFFSET + 5 , " Invalid spawn packet received " ) ;
ERR_FAIL_COND_MSG ( ! replications . has ( p_id ) , " Invalid spawn ID received " + itos ( p_id ) ) ;
SceneConfig & cfg = replications [ p_id ] ;
ERR_FAIL_COND_MSG ( cfg . mode ! = REPLICATION_MODE_SERVER | | multiplayer - > is_network_server ( ) , " The defualt implementation only allows sync packets from the server " ) ;
2021-09-03 19:40:47 +02:00
const bool same_size = p_packet [ 0 ] & BYTE_OR_ZERO_FLAG ;
2021-08-12 21:07:44 +02:00
int ofs = SYNC_CMD_OFFSET ;
int time = p_packet [ ofs ] ;
// Skip old update.
if ( time < cfg . sync_recv & & cfg . sync_recv - time < 127 ) {
return ;
}
cfg . sync_recv = time ;
ofs + = 1 ;
int count = decode_uint16 ( & p_packet [ ofs ] ) ;
ofs + = 2 ;
# ifdef DEBUG_ENABLED
ERR_FAIL_COND ( ! tracked_objects . has ( p_id ) | | tracked_objects [ p_id ] . size ( ) ! = count ) ;
# else
if ( ! tracked_objects . has ( p_id ) | | tracked_objects [ p_id ] . size ( ) ! = count ) {
return ;
}
# endif
int data_size = 0 ;
bool raw = false ;
if ( same_size ) {
// This is fast and optimized.
data_size = decode_uint16 ( & p_packet [ ofs ] ) ;
raw = ( data_size & ( 1 < < 15 ) ) ! = 0 ;
data_size = data_size & ~ ( 1 < < 15 ) ;
ofs + = 2 ;
ERR_FAIL_COND ( p_packet_len - ofs < data_size * count ) ;
}
for ( const ObjectID & obj_id : tracked_objects [ p_id ] ) {
Object * obj = ObjectDB : : get_instance ( obj_id ) ;
ERR_CONTINUE ( ! obj ) ;
if ( ! same_size ) {
// This is slow and wasteful.
data_size = decode_uint16 ( & p_packet [ ofs ] ) ;
raw = ( data_size & ( 1 < < 15 ) ) ! = 0 ;
data_size = data_size & ~ ( 1 < < 15 ) ;
ofs + = 2 ;
ERR_FAIL_COND ( p_packet_len - ofs < data_size ) ;
}
int size = 0 ;
Error err = _decode_state ( cfg . sync_properties , obj , & p_packet [ ofs ] , data_size , size , raw ) ;
ofs + = data_size ;
ERR_CONTINUE ( err ) ;
ERR_CONTINUE ( size ! = data_size ) ;
}
}
2021-08-09 22:46:23 +02:00
Error MultiplayerReplicator : : _send_default_spawn_despawn ( int p_peer_id , const ResourceUID : : ID & p_scene_id , Object * p_obj , const NodePath & p_path , bool p_spawn ) {
ERR_FAIL_COND_V ( p_spawn & & ! p_obj , ERR_INVALID_PARAMETER ) ;
ERR_FAIL_COND_V ( ! replications . has ( p_scene_id ) , ERR_INVALID_PARAMETER ) ;
Error err ;
// Prepare state
List < Variant > state_variants ;
int state_len = 0 ;
const SceneConfig & cfg = replications [ p_scene_id ] ;
if ( p_spawn ) {
if ( ( err = _get_state ( cfg . properties , p_obj , state_variants ) ) ! = OK ) {
return err ;
}
}
bool is_raw = false ;
if ( state_variants . size ( ) = = 1 & & state_variants [ 0 ] . get_type ( ) = = Variant : : PACKED_BYTE_ARRAY ) {
is_raw = true ;
2021-09-02 22:32:03 +02:00
const PackedByteArray pba = state_variants [ 0 ] ;
state_len = pba . size ( ) ;
2021-08-09 22:46:23 +02:00
} else if ( state_variants . size ( ) ) {
err = _encode_state ( state_variants , nullptr , state_len ) ;
ERR_FAIL_COND_V ( err , err ) ;
} else {
is_raw = true ;
}
int ofs = 0 ;
// Prepare simplified path
const Node * root_node = multiplayer - > get_root_node ( ) ;
ERR_FAIL_COND_V ( ! root_node , ERR_UNCONFIGURED ) ;
NodePath rel_path = ( root_node - > get_path ( ) ) . rel_path_to ( p_path ) ;
const Vector < StringName > names = rel_path . get_names ( ) ;
ERR_FAIL_COND_V ( names . size ( ) < 2 , ERR_INVALID_PARAMETER ) ;
NodePath parent = NodePath ( names . subarray ( 0 , names . size ( ) - 2 ) , false ) ;
ERR_FAIL_COND_V_MSG ( ! root_node - > has_node ( parent ) , ERR_INVALID_PARAMETER , " Path not found: " + parent ) ;
int path_id = 0 ;
multiplayer - > send_confirm_path ( root_node - > get_node ( parent ) , parent , p_peer_id , path_id ) ;
// Encode name and parent ID.
CharString cname = String ( names [ names . size ( ) - 1 ] ) . utf8 ( ) ;
int nlen = encode_cstring ( cname . get_data ( ) , nullptr ) ;
MAKE_ROOM ( SPAWN_CMD_OFFSET + 4 + 4 + nlen + state_len ) ;
uint8_t * ptr = packet_cache . ptrw ( ) ;
2021-09-03 19:40:47 +02:00
ptr [ 0 ] = ( p_spawn ? MultiplayerAPI : : NETWORK_COMMAND_SPAWN : MultiplayerAPI : : NETWORK_COMMAND_DESPAWN ) | ( is_raw ? BYTE_OR_ZERO_FLAG : 0 ) ;
2021-08-09 22:46:23 +02:00
ofs = 1 ;
ofs + = encode_uint64 ( p_scene_id , & ptr [ ofs ] ) ;
ofs + = encode_uint32 ( path_id , & ptr [ ofs ] ) ;
ofs + = encode_uint32 ( nlen , & ptr [ ofs ] ) ;
ofs + = encode_cstring ( cname . get_data ( ) , & ptr [ ofs ] ) ;
// Encode state.
if ( ! is_raw ) {
_encode_state ( state_variants , & ptr [ ofs ] , state_len ) ;
} else if ( state_len ) {
PackedByteArray pba = state_variants [ 0 ] ;
memcpy ( & ptr [ ofs ] , pba . ptr ( ) , state_len ) ;
}
Ref < MultiplayerPeer > network_peer = multiplayer - > get_network_peer ( ) ;
network_peer - > set_target_peer ( p_peer_id ) ;
network_peer - > set_transfer_channel ( 0 ) ;
2021-09-03 19:40:47 +02:00
network_peer - > set_transfer_mode ( Multiplayer : : TRANSFER_MODE_RELIABLE ) ;
2021-08-09 22:46:23 +02:00
return network_peer - > put_packet ( ptr , ofs + state_len ) ;
}
void MultiplayerReplicator : : _process_default_spawn_despawn ( int p_from , const ResourceUID : : ID & p_scene_id , const uint8_t * p_packet , int p_packet_len , bool p_spawn ) {
ERR_FAIL_COND_MSG ( p_packet_len < SPAWN_CMD_OFFSET + 9 , " Invalid spawn packet received " ) ;
int ofs = SPAWN_CMD_OFFSET ;
uint32_t node_target = decode_uint32 ( & p_packet [ ofs ] ) ;
Node * parent = multiplayer - > get_cached_node ( p_from , node_target ) ;
ofs + = 4 ;
ERR_FAIL_COND_MSG ( parent = = nullptr , " Invalid packet received. Requested node was not found. " ) ;
uint32_t name_len = decode_uint32 ( & p_packet [ ofs ] ) ;
ofs + = 4 ;
ERR_FAIL_COND_MSG ( name_len > uint32_t ( p_packet_len - ofs ) , vformat ( " Invalid spawn packet size: %d, wants: %d " , p_packet_len , ofs + name_len ) ) ;
ERR_FAIL_COND_MSG ( name_len < 1 , " Zero spawn name size. " ) ;
const String name = String : : utf8 ( ( const char * ) & p_packet [ ofs ] , name_len ) ;
// We need to make sure no trickery happens here (e.g. despawning a subpath), but we want to allow autogenerated ("@") node names.
ERR_FAIL_COND_MSG ( name . validate_node_name ( ) ! = name . replace ( " @ " , " " ) , vformat ( " Invalid node name received: '%s' " , name ) ) ;
ofs + = name_len ;
const SceneConfig & cfg = replications [ p_scene_id ] ;
if ( cfg . mode = = REPLICATION_MODE_SERVER & & p_from = = 1 ) {
String scene_path = ResourceUID : : get_singleton ( ) - > get_id_path ( p_scene_id ) ;
if ( p_spawn ) {
2021-09-03 19:40:47 +02:00
const bool is_raw = ( ( p_packet [ 0 ] & BYTE_OR_ZERO_FLAG ) > > BYTE_OR_ZERO_SHIFT ) = = 1 ;
2021-08-09 22:46:23 +02:00
ERR_FAIL_COND_MSG ( parent - > has_node ( name ) , vformat ( " Unable to spawn node. Node already exists: %s/%s " , parent - > get_path ( ) , name ) ) ;
RES res = ResourceLoader : : load ( scene_path ) ;
ERR_FAIL_COND_MSG ( ! res . is_valid ( ) , " Unable to load scene to spawn at path: " + scene_path ) ;
PackedScene * scene = Object : : cast_to < PackedScene > ( res . ptr ( ) ) ;
ERR_FAIL_COND ( ! scene ) ;
Node * node = scene - > instantiate ( ) ;
ERR_FAIL_COND ( ! node ) ;
replicated_nodes [ node - > get_instance_id ( ) ] = p_scene_id ;
2021-08-12 21:07:44 +02:00
_track ( p_scene_id , node ) ;
2021-08-09 22:46:23 +02:00
int size ;
_decode_state ( cfg . properties , node , & p_packet [ ofs ] , p_packet_len - ofs , size , is_raw ) ;
parent - > _add_child_nocheck ( node , name ) ;
emit_signal ( SNAME ( " spawned " ) , p_scene_id , node ) ;
} else {
ERR_FAIL_COND_MSG ( ! parent - > has_node ( name ) , vformat ( " Path not found: %s/%s " , parent - > get_path ( ) , name ) ) ;
Node * node = parent - > get_node ( name ) ;
ERR_FAIL_COND_MSG ( ! replicated_nodes . has ( node - > get_instance_id ( ) ) , vformat ( " Trying to despawn a Node that was not replicated: %s/%s " , parent - > get_path ( ) , name ) ) ;
emit_signal ( SNAME ( " despawned " ) , p_scene_id , node ) ;
2021-08-12 21:07:44 +02:00
_untrack ( p_scene_id , node ) ;
2021-08-09 22:46:23 +02:00
replicated_nodes . erase ( node - > get_instance_id ( ) ) ;
node - > queue_delete ( ) ;
}
} else {
PackedByteArray data ;
if ( p_packet_len > ofs ) {
data . resize ( p_packet_len - ofs ) ;
memcpy ( data . ptrw ( ) , & p_packet [ ofs ] , data . size ( ) ) ;
}
if ( p_spawn ) {
emit_signal ( SNAME ( " spawn_requested " ) , p_from , p_scene_id , parent , name , data ) ;
} else {
emit_signal ( SNAME ( " despawn_requested " ) , p_from , p_scene_id , parent , name , data ) ;
}
}
}
void MultiplayerReplicator : : process_spawn_despawn ( int p_from , const uint8_t * p_packet , int p_packet_len , bool p_spawn ) {
ERR_FAIL_COND_MSG ( p_packet_len < SPAWN_CMD_OFFSET , " Invalid spawn packet received " ) ;
ResourceUID : : ID id = decode_uint64 ( & p_packet [ 1 ] ) ;
ERR_FAIL_COND_MSG ( ! replications . has ( id ) , " Invalid spawn ID received " + itos ( id ) ) ;
const SceneConfig & cfg = replications [ id ] ;
if ( cfg . on_spawn_despawn_receive . is_valid ( ) ) {
int ofs = SPAWN_CMD_OFFSET ;
2021-09-03 19:40:47 +02:00
bool is_raw = ( ( p_packet [ 0 ] & BYTE_OR_ZERO_FLAG ) > > BYTE_OR_ZERO_SHIFT ) = = 1 ;
2021-08-09 22:46:23 +02:00
Variant data ;
int left = p_packet_len - ofs ;
if ( is_raw & & left ) {
PackedByteArray pba ;
pba . resize ( left ) ;
memcpy ( pba . ptrw ( ) , & p_packet [ ofs ] , pba . size ( ) ) ;
data = pba ;
} else if ( left ) {
ERR_FAIL_COND ( decode_variant ( data , & p_packet [ ofs ] , left ) ! = OK ) ;
}
Variant args [ 4 ] ;
args [ 0 ] = p_from ;
args [ 1 ] = id ;
args [ 2 ] = data ;
args [ 3 ] = p_spawn ;
const Variant * argp [ ] = { & args [ 0 ] , & args [ 1 ] , & args [ 2 ] , & args [ 3 ] } ;
Callable : : CallError ce ;
Variant ret ;
cfg . on_spawn_despawn_receive . call ( argp , 4 , ret , ce ) ;
ERR_FAIL_COND_MSG ( ce . error ! = Callable : : CallError : : CALL_OK , " Custom receive function failed " ) ;
} else {
_process_default_spawn_despawn ( p_from , id , p_packet , p_packet_len , p_spawn ) ;
}
}
2021-08-12 21:07:44 +02:00
void MultiplayerReplicator : : process_sync ( int p_from , const uint8_t * p_packet , int p_packet_len ) {
ERR_FAIL_COND_MSG ( p_packet_len < SPAWN_CMD_OFFSET , " Invalid spawn packet received " ) ;
ResourceUID : : ID id = decode_uint64 ( & p_packet [ 1 ] ) ;
ERR_FAIL_COND_MSG ( ! replications . has ( id ) , " Invalid spawn ID received " + itos ( id ) ) ;
const SceneConfig & cfg = replications [ id ] ;
if ( cfg . on_sync_receive . is_valid ( ) ) {
Array objs ;
if ( tracked_objects . has ( id ) ) {
2021-08-26 14:35:33 +02:00
objs . resize ( tracked_objects [ id ] . size ( ) ) ;
int idx = 0 ;
2021-08-12 21:07:44 +02:00
for ( const ObjectID & obj_id : tracked_objects [ id ] ) {
2021-08-26 14:35:33 +02:00
objs [ idx + + ] = ObjectDB : : get_instance ( obj_id ) ;
2021-08-12 21:07:44 +02:00
}
}
PackedByteArray pba ;
pba . resize ( p_packet_len - SPAWN_CMD_OFFSET ) ;
if ( pba . size ( ) ) {
memcpy ( pba . ptrw ( ) , p_packet , p_packet_len - SPAWN_CMD_OFFSET ) ;
}
Variant args [ 4 ] = { p_from , id , objs , pba } ;
Variant * argp [ 4 ] = { args , & args [ 1 ] , & args [ 2 ] , & args [ 3 ] } ;
Callable : : CallError ce ;
Variant ret ;
cfg . on_sync_receive . call ( ( const Variant * * ) argp , 4 , ret , ce ) ;
ERR_FAIL_COND_MSG ( ce . error ! = Callable : : CallError : : CALL_OK , " Custom sync function failed " ) ;
} else {
ERR_FAIL_COND_MSG ( p_from ! = 1 , " Default sync implementation only allow syncing from server to client " ) ;
_process_default_sync ( id , p_packet , p_packet_len ) ;
}
}
2021-08-09 22:46:23 +02:00
Error MultiplayerReplicator : : _get_state ( const List < StringName > & p_properties , const Object * p_obj , List < Variant > & r_variant ) {
ERR_FAIL_COND_V_MSG ( ! p_obj , ERR_INVALID_PARAMETER , " Cannot encode null object " ) ;
for ( const StringName & prop : p_properties ) {
bool valid = false ;
const Variant v = p_obj - > get ( prop , & valid ) ;
ERR_FAIL_COND_V_MSG ( ! valid , ERR_INVALID_DATA , vformat ( " Property '%s' not found. " , prop ) ) ;
r_variant . push_back ( v ) ;
}
return OK ;
}
Error MultiplayerReplicator : : _encode_state ( const List < Variant > & p_variants , uint8_t * p_buffer , int & r_len , bool * r_raw ) {
r_len = 0 ;
int size = 0 ;
// Try raw encoding optimization.
if ( r_raw & & p_variants . size ( ) = = 1 ) {
* r_raw = false ;
const Variant v = p_variants [ 0 ] ;
if ( v . get_type ( ) = = Variant : : PACKED_BYTE_ARRAY ) {
* r_raw = true ;
const PackedByteArray pba = v ;
if ( p_buffer ) {
memcpy ( p_buffer , pba . ptr ( ) , pba . size ( ) ) ;
}
r_len + = pba . size ( ) ;
} else {
multiplayer - > encode_and_compress_variant ( v , p_buffer , size ) ;
r_len + = size ;
}
return OK ;
}
// Regular encoding.
for ( const Variant & v : p_variants ) {
multiplayer - > encode_and_compress_variant ( v , p_buffer ? p_buffer + r_len : nullptr , size ) ;
r_len + = size ;
}
return OK ;
}
Error MultiplayerReplicator : : _decode_state ( const List < StringName > & p_properties , Object * p_obj , const uint8_t * p_buffer , int p_len , int & r_len , bool p_raw ) {
r_len = 0 ;
int argc = p_properties . size ( ) ;
if ( argc = = 0 & & p_raw ) {
ERR_FAIL_COND_V_MSG ( p_len ! = 0 , ERR_INVALID_DATA , " Buffer has trailing bytes. " ) ;
return OK ;
}
ERR_FAIL_COND_V ( p_raw & & argc ! = 1 , ERR_INVALID_DATA ) ;
if ( p_raw ) {
r_len = p_len ;
PackedByteArray pba ;
pba . resize ( p_len ) ;
memcpy ( pba . ptrw ( ) , p_buffer , p_len ) ;
p_obj - > set ( p_properties [ 0 ] , pba ) ;
return OK ;
}
Vector < Variant > args ;
Vector < const Variant * > argp ;
args . resize ( argc ) ;
for ( int i = 0 ; i < argc ; i + + ) {
ERR_FAIL_COND_V_MSG ( r_len > = p_len , ERR_INVALID_DATA , " Invalid packet received. Size too small. " ) ;
int vlen ;
Error err = multiplayer - > decode_and_decompress_variant ( args . write [ i ] , & p_buffer [ r_len ] , p_len - r_len , & vlen ) ;
ERR_FAIL_COND_V_MSG ( err ! = OK , err , " Invalid packet received. Unable to decode state variable. " ) ;
r_len + = vlen ;
}
ERR_FAIL_COND_V_MSG ( p_len - r_len ! = 0 , ERR_INVALID_DATA , " Buffer has trailing bytes. " ) ;
int i = 0 ;
for ( const StringName & prop : p_properties ) {
p_obj - > set ( prop , args [ i ] ) ;
i + = 1 ;
}
return OK ;
}
Error MultiplayerReplicator : : spawn_config ( const ResourceUID : : ID & p_id , ReplicationMode p_mode , const TypedArray < StringName > & p_props , const Callable & p_on_send , const Callable & p_on_recv ) {
ERR_FAIL_COND_V ( p_mode < REPLICATION_MODE_NONE | | p_mode > REPLICATION_MODE_CUSTOM , ERR_INVALID_PARAMETER ) ;
ERR_FAIL_COND_V ( ! ResourceUID : : get_singleton ( ) - > has_id ( p_id ) , ERR_INVALID_PARAMETER ) ;
ERR_FAIL_COND_V_MSG ( p_on_send . is_valid ( ) ! = p_on_recv . is_valid ( ) , ERR_INVALID_PARAMETER , " Send and receive custom callables must be both valid or both empty " ) ;
# ifdef TOOLS_ENABLED
if ( ! p_on_send . is_valid ( ) ) {
// We allow non scene spawning with custom callables.
String path = ResourceUID : : get_singleton ( ) - > get_id_path ( p_id ) ;
RES res = ResourceLoader : : load ( path ) ;
ERR_FAIL_COND_V ( ! res - > is_class ( " PackedScene " ) , ERR_INVALID_PARAMETER ) ;
}
# endif
if ( p_mode = = REPLICATION_MODE_NONE ) {
if ( replications . has ( p_id ) ) {
replications . erase ( p_id ) ;
}
} else {
SceneConfig cfg ;
cfg . mode = p_mode ;
for ( int i = 0 ; i < p_props . size ( ) ; i + + ) {
2021-09-03 19:40:47 +02:00
cfg . properties . push_back ( p_props [ i ] ) ;
2021-08-09 22:46:23 +02:00
}
cfg . on_spawn_despawn_send = p_on_send ;
cfg . on_spawn_despawn_receive = p_on_recv ;
replications [ p_id ] = cfg ;
}
return OK ;
}
2021-08-12 21:07:44 +02:00
Error MultiplayerReplicator : : sync_config ( const ResourceUID : : ID & p_id , uint64_t p_interval , const TypedArray < StringName > & p_props , const Callable & p_on_send , const Callable & p_on_recv ) {
ERR_FAIL_COND_V ( ! ResourceUID : : get_singleton ( ) - > has_id ( p_id ) , ERR_INVALID_PARAMETER ) ;
ERR_FAIL_COND_V_MSG ( p_on_send . is_valid ( ) ! = p_on_recv . is_valid ( ) , ERR_INVALID_PARAMETER , " Send and receive custom callables must be both valid or both empty " ) ;
ERR_FAIL_COND_V ( ! replications . has ( p_id ) , ERR_UNCONFIGURED ) ;
SceneConfig & cfg = replications [ p_id ] ;
ERR_FAIL_COND_V_MSG ( p_interval & & cfg . mode ! = REPLICATION_MODE_SERVER & & ! p_on_send . is_valid ( ) , ERR_INVALID_PARAMETER , " Timed updates in custom mode are only allowed if custom callbacks are also specified " ) ;
for ( int i = 0 ; i < p_props . size ( ) ; i + + ) {
cfg . sync_properties . push_back ( p_props [ i ] ) ;
}
cfg . on_sync_send = p_on_send ;
cfg . on_sync_receive = p_on_recv ;
cfg . sync_interval = p_interval * 1000 ;
return OK ;
}
2021-08-09 22:46:23 +02:00
Error MultiplayerReplicator : : _send_spawn_despawn ( int p_peer_id , const ResourceUID : : ID & p_scene_id , const Variant & p_data , bool p_spawn ) {
int data_size = 0 ;
int is_raw = false ;
if ( p_data . get_type ( ) = = Variant : : PACKED_BYTE_ARRAY ) {
const PackedByteArray pba = p_data ;
is_raw = true ;
data_size = p_data . operator PackedByteArray ( ) . size ( ) ;
} else if ( p_data . get_type ( ) = = Variant : : NIL ) {
is_raw = true ;
} else {
Error err = encode_variant ( p_data , nullptr , data_size ) ;
ERR_FAIL_COND_V ( err , err ) ;
}
MAKE_ROOM ( SPAWN_CMD_OFFSET + data_size ) ;
uint8_t * ptr = packet_cache . ptrw ( ) ;
2021-09-03 19:40:47 +02:00
ptr [ 0 ] = ( p_spawn ? MultiplayerAPI : : NETWORK_COMMAND_SPAWN : MultiplayerAPI : : NETWORK_COMMAND_DESPAWN ) + ( ( is_raw ? 1 : 0 ) < < BYTE_OR_ZERO_SHIFT ) ;
2021-08-09 22:46:23 +02:00
encode_uint64 ( p_scene_id , & ptr [ 1 ] ) ;
if ( p_data . get_type ( ) = = Variant : : PACKED_BYTE_ARRAY ) {
const PackedByteArray pba = p_data ;
memcpy ( & ptr [ SPAWN_CMD_OFFSET ] , pba . ptr ( ) , pba . size ( ) ) ;
} else if ( data_size ) {
encode_variant ( p_data , & ptr [ SPAWN_CMD_OFFSET ] , data_size ) ;
}
Ref < MultiplayerPeer > network_peer = multiplayer - > get_network_peer ( ) ;
network_peer - > set_target_peer ( p_peer_id ) ;
network_peer - > set_transfer_channel ( 0 ) ;
2021-09-03 19:40:47 +02:00
network_peer - > set_transfer_mode ( Multiplayer : : TRANSFER_MODE_RELIABLE ) ;
2021-08-09 22:46:23 +02:00
return network_peer - > put_packet ( ptr , SPAWN_CMD_OFFSET + data_size ) ;
}
Error MultiplayerReplicator : : send_despawn ( int p_peer_id , const ResourceUID : : ID & p_scene_id , const Variant & p_data , const NodePath & p_path ) {
2021-08-12 21:07:44 +02:00
ERR_FAIL_COND_V ( ! multiplayer - > has_network_peer ( ) , ERR_UNCONFIGURED ) ;
2021-08-09 22:46:23 +02:00
ERR_FAIL_COND_V_MSG ( ! replications . has ( p_scene_id ) , ERR_INVALID_PARAMETER , vformat ( " Spawnable not found: %d " , p_scene_id ) ) ;
const SceneConfig & cfg = replications [ p_scene_id ] ;
if ( cfg . on_spawn_despawn_send . is_valid ( ) ) {
return _send_spawn_despawn ( p_peer_id , p_scene_id , p_data , true ) ;
} else {
ERR_FAIL_COND_V_MSG ( cfg . mode = = REPLICATION_MODE_SERVER & & multiplayer - > is_network_server ( ) , ERR_UNAVAILABLE , " Manual despawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests. " ) ;
NodePath path = p_path ;
Object * obj = p_data . get_type ( ) = = Variant : : OBJECT ? p_data . get_validated_object ( ) : nullptr ;
if ( path . is_empty ( ) & & obj ) {
Node * node = Object : : cast_to < Node > ( obj ) ;
if ( node & & node - > is_inside_tree ( ) ) {
path = node - > get_path ( ) ;
}
}
ERR_FAIL_COND_V_MSG ( path . is_empty ( ) , ERR_INVALID_PARAMETER , " Despawn default implementation requires a despawn path, or the data to be a node inside the SceneTree " ) ;
return _send_default_spawn_despawn ( p_peer_id , p_scene_id , obj , path , false ) ;
}
}
Error MultiplayerReplicator : : send_spawn ( int p_peer_id , const ResourceUID : : ID & p_scene_id , const Variant & p_data , const NodePath & p_path ) {
2021-08-12 21:07:44 +02:00
ERR_FAIL_COND_V ( ! multiplayer - > has_network_peer ( ) , ERR_UNCONFIGURED ) ;
2021-08-09 22:46:23 +02:00
ERR_FAIL_COND_V_MSG ( ! replications . has ( p_scene_id ) , ERR_INVALID_PARAMETER , vformat ( " Spawnable not found: %d " , p_scene_id ) ) ;
const SceneConfig & cfg = replications [ p_scene_id ] ;
if ( cfg . on_spawn_despawn_send . is_valid ( ) ) {
return _send_spawn_despawn ( p_peer_id , p_scene_id , p_data , false ) ;
} else {
ERR_FAIL_COND_V_MSG ( cfg . mode = = REPLICATION_MODE_SERVER & & multiplayer - > is_network_server ( ) , ERR_UNAVAILABLE , " Manual spawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests. " ) ;
NodePath path = p_path ;
Object * obj = p_data . get_type ( ) = = Variant : : OBJECT ? p_data . get_validated_object ( ) : nullptr ;
ERR_FAIL_COND_V_MSG ( ! obj , ERR_INVALID_PARAMETER , " Spawn default implementation requires the data to be an object. " ) ;
if ( path . is_empty ( ) ) {
Node * node = Object : : cast_to < Node > ( obj ) ;
if ( node & & node - > is_inside_tree ( ) ) {
path = node - > get_path ( ) ;
}
}
ERR_FAIL_COND_V_MSG ( path . is_empty ( ) , ERR_INVALID_PARAMETER , " Spawn default implementation requires a spawn path, or the data to be a node inside the SceneTree " ) ;
return _send_default_spawn_despawn ( p_peer_id , p_scene_id , obj , path , true ) ;
}
}
Error MultiplayerReplicator : : _spawn_despawn ( ResourceUID : : ID p_scene_id , Object * p_obj , int p_peer , bool p_spawn ) {
ERR_FAIL_COND_V_MSG ( ! replications . has ( p_scene_id ) , ERR_INVALID_PARAMETER , vformat ( " Spawnable not found: %d " , p_scene_id ) ) ;
const SceneConfig & cfg = replications [ p_scene_id ] ;
if ( cfg . on_spawn_despawn_send . is_valid ( ) ) {
Variant args [ 4 ] ;
args [ 0 ] = p_peer ;
args [ 1 ] = p_scene_id ;
args [ 2 ] = p_obj ;
args [ 3 ] = true ;
const Variant * argp [ ] = { & args [ 0 ] , & args [ 1 ] , & args [ 2 ] , & args [ 3 ] } ;
Callable : : CallError ce ;
Variant ret ;
cfg . on_spawn_despawn_send . call ( argp , 4 , ret , ce ) ;
ERR_FAIL_COND_V_MSG ( ce . error ! = Callable : : CallError : : CALL_OK , FAILED , " Custom send function failed " ) ;
return OK ;
} else {
Node * node = Object : : cast_to < Node > ( p_obj ) ;
ERR_FAIL_COND_V_MSG ( ! p_obj , ERR_INVALID_PARAMETER , " Only nodes can be replicated by the default implementation " ) ;
return _send_default_spawn_despawn ( p_peer , p_scene_id , node , node - > get_path ( ) , p_spawn ) ;
}
}
Error MultiplayerReplicator : : spawn ( ResourceUID : : ID p_scene_id , Object * p_obj , int p_peer ) {
return _spawn_despawn ( p_scene_id , p_obj , p_peer , true ) ;
}
Error MultiplayerReplicator : : despawn ( ResourceUID : : ID p_scene_id , Object * p_obj , int p_peer ) {
return _spawn_despawn ( p_scene_id , p_obj , p_peer , false ) ;
}
2021-08-12 21:07:44 +02:00
PackedByteArray MultiplayerReplicator : : encode_state ( const ResourceUID : : ID & p_scene_id , const Object * p_obj , bool p_initial ) {
2021-08-09 22:46:23 +02:00
PackedByteArray state ;
ERR_FAIL_COND_V_MSG ( ! replications . has ( p_scene_id ) , state , vformat ( " Spawnable not found: %d " , p_scene_id ) ) ;
const SceneConfig & cfg = replications [ p_scene_id ] ;
int len = 0 ;
List < Variant > state_vars ;
2021-08-12 21:07:44 +02:00
const List < StringName > props = p_initial ? cfg . properties : cfg . sync_properties ;
Error err = _get_state ( props , p_obj , state_vars ) ;
2021-08-09 22:46:23 +02:00
ERR_FAIL_COND_V_MSG ( err ! = OK , state , " Unable to retrieve object state. " ) ;
err = _encode_state ( state_vars , nullptr , len ) ;
ERR_FAIL_COND_V_MSG ( err ! = OK , state , " Unable to encode object state. " ) ;
state . resize ( len ) ;
_encode_state ( state_vars , state . ptrw ( ) , len ) ;
return state ;
}
2021-08-12 21:07:44 +02:00
Error MultiplayerReplicator : : decode_state ( const ResourceUID : : ID & p_scene_id , Object * p_obj , const PackedByteArray p_data , bool p_initial ) {
2021-08-09 22:46:23 +02:00
ERR_FAIL_COND_V_MSG ( ! replications . has ( p_scene_id ) , ERR_INVALID_PARAMETER , vformat ( " Spawnable not found: %d " , p_scene_id ) ) ;
const SceneConfig & cfg = replications [ p_scene_id ] ;
2021-08-12 21:07:44 +02:00
const List < StringName > props = p_initial ? cfg . properties : cfg . sync_properties ;
2021-08-09 22:46:23 +02:00
int size ;
2021-08-12 21:07:44 +02:00
return _decode_state ( props , p_obj , p_data . ptr ( ) , p_data . size ( ) , size ) ;
2021-08-09 22:46:23 +02:00
}
void MultiplayerReplicator : : scene_enter_exit_notify ( const String & p_scene , Node * p_node , bool p_enter ) {
if ( ! multiplayer - > has_network_peer ( ) ) {
return ;
}
Node * root_node = multiplayer - > get_root_node ( ) ;
ERR_FAIL_COND ( ! p_node | | ! p_node - > get_parent ( ) | | ! root_node ) ;
NodePath path = ( root_node - > get_path ( ) ) . rel_path_to ( p_node - > get_parent ( ) - > get_path ( ) ) ;
if ( path . is_empty ( ) ) {
return ;
}
ResourceUID : : ID id = ResourceLoader : : get_resource_uid ( p_scene ) ;
if ( ! replications . has ( id ) ) {
return ;
}
const SceneConfig & cfg = replications [ id ] ;
if ( p_enter ) {
if ( cfg . mode = = REPLICATION_MODE_SERVER & & multiplayer - > is_network_server ( ) ) {
replicated_nodes [ p_node - > get_instance_id ( ) ] = id ;
2021-08-12 21:07:44 +02:00
_track ( id , p_node ) ;
2021-08-09 22:46:23 +02:00
spawn ( id , p_node , 0 ) ;
}
emit_signal ( SNAME ( " replicated_instance_added " ) , id , p_node ) ;
} else {
if ( cfg . mode = = REPLICATION_MODE_SERVER & & multiplayer - > is_network_server ( ) & & replicated_nodes . has ( p_node - > get_instance_id ( ) ) ) {
replicated_nodes . erase ( p_node - > get_instance_id ( ) ) ;
2021-08-12 21:07:44 +02:00
_untrack ( id , p_node ) ;
2021-08-09 22:46:23 +02:00
despawn ( id , p_node , 0 ) ;
}
emit_signal ( SNAME ( " replicated_instance_removed " ) , id , p_node ) ;
}
}
void MultiplayerReplicator : : spawn_all ( int p_peer ) {
for ( const KeyValue < ObjectID , ResourceUID : : ID > & E : replicated_nodes ) {
// Only server mode adds to replicated_nodes, no need to check it.
Object * obj = ObjectDB : : get_instance ( E . key ) ;
ERR_CONTINUE ( ! obj ) ;
Node * node = Object : : cast_to < Node > ( obj ) ;
ERR_CONTINUE ( ! node ) ;
spawn ( E . value , node , p_peer ) ;
}
}
2021-08-12 21:07:44 +02:00
void MultiplayerReplicator : : poll ( ) {
for ( KeyValue < ResourceUID : : ID , SceneConfig > & E : replications ) {
if ( ! E . value . sync_interval ) {
continue ;
}
if ( E . value . mode = = REPLICATION_MODE_SERVER & & ! multiplayer - > is_network_server ( ) ) {
continue ;
}
uint64_t time = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
if ( E . value . sync_last + E . value . sync_interval < = time ) {
sync_all ( E . key , 0 ) ;
E . value . sync_last = time ;
}
// Handle wrapping.
if ( E . value . sync_last > time ) {
E . value . sync_last = time ;
}
}
}
void MultiplayerReplicator : : track ( const ResourceUID : : ID & p_scene_id , Object * p_obj ) {
ERR_FAIL_COND ( ! replications . has ( p_scene_id ) ) ;
const SceneConfig & cfg = replications [ p_scene_id ] ;
ERR_FAIL_COND_MSG ( cfg . mode = = REPLICATION_MODE_SERVER , " Manual object tracking is not allowed in server mode. " ) ;
_track ( p_scene_id , p_obj ) ;
}
void MultiplayerReplicator : : _track ( const ResourceUID : : ID & p_scene_id , Object * p_obj ) {
ERR_FAIL_COND ( ! p_obj ) ;
ERR_FAIL_COND ( ! replications . has ( p_scene_id ) ) ;
if ( ! tracked_objects . has ( p_scene_id ) ) {
tracked_objects [ p_scene_id ] = List < ObjectID > ( ) ;
}
tracked_objects [ p_scene_id ] . push_back ( p_obj - > get_instance_id ( ) ) ;
}
void MultiplayerReplicator : : untrack ( const ResourceUID : : ID & p_scene_id , Object * p_obj ) {
ERR_FAIL_COND ( ! replications . has ( p_scene_id ) ) ;
const SceneConfig & cfg = replications [ p_scene_id ] ;
ERR_FAIL_COND_MSG ( cfg . mode = = REPLICATION_MODE_SERVER , " Manual object tracking is not allowed in server mode. " ) ;
_untrack ( p_scene_id , p_obj ) ;
}
void MultiplayerReplicator : : _untrack ( const ResourceUID : : ID & p_scene_id , Object * p_obj ) {
ERR_FAIL_COND ( ! p_obj ) ;
ERR_FAIL_COND ( ! replications . has ( p_scene_id ) ) ;
if ( tracked_objects . has ( p_scene_id ) ) {
tracked_objects [ p_scene_id ] . erase ( p_obj - > get_instance_id ( ) ) ;
}
}
Error MultiplayerReplicator : : sync_all ( const ResourceUID : : ID & p_scene_id , int p_peer ) {
ERR_FAIL_COND_V ( ! replications . has ( p_scene_id ) , ERR_INVALID_PARAMETER ) ;
if ( ! tracked_objects . has ( p_scene_id ) ) {
return OK ;
}
const SceneConfig & cfg = replications [ p_scene_id ] ;
if ( cfg . on_sync_send . is_valid ( ) ) {
Array objs ;
if ( tracked_objects . has ( p_scene_id ) ) {
2021-08-26 14:35:33 +02:00
objs . resize ( tracked_objects [ p_scene_id ] . size ( ) ) ;
int idx = 0 ;
2021-08-12 21:07:44 +02:00
for ( const ObjectID & obj_id : tracked_objects [ p_scene_id ] ) {
2021-08-26 14:35:33 +02:00
objs [ idx + + ] = ObjectDB : : get_instance ( obj_id ) ;
2021-08-12 21:07:44 +02:00
}
}
Variant args [ 3 ] = { p_scene_id , objs , p_peer } ;
Variant * argp [ 3 ] = { args , & args [ 1 ] , & args [ 2 ] } ;
Callable : : CallError ce ;
Variant ret ;
cfg . on_sync_send . call ( ( const Variant * * ) argp , 3 , ret , ce ) ;
ERR_FAIL_COND_V_MSG ( ce . error ! = Callable : : CallError : : CALL_OK , FAILED , " Custom sync function failed " ) ;
return OK ;
} else if ( cfg . sync_properties . size ( ) ) {
return _sync_all_default ( p_scene_id , p_peer ) ;
}
return OK ;
}
2021-09-03 19:40:47 +02:00
Error MultiplayerReplicator : : send_sync ( int p_peer_id , const ResourceUID : : ID & p_scene_id , PackedByteArray p_data , Multiplayer : : TransferMode p_transfer_mode , int p_channel ) {
2021-08-12 21:07:44 +02:00
ERR_FAIL_COND_V ( ! multiplayer - > has_network_peer ( ) , ERR_UNCONFIGURED ) ;
ERR_FAIL_COND_V ( ! replications . has ( p_scene_id ) , ERR_INVALID_PARAMETER ) ;
const SceneConfig & cfg = replications [ p_scene_id ] ;
ERR_FAIL_COND_V_MSG ( ! cfg . on_sync_send . is_valid ( ) , ERR_UNCONFIGURED , " Sending raw sync messages is only available with custom functions " ) ;
MAKE_ROOM ( SYNC_CMD_OFFSET + p_data . size ( ) ) ;
uint8_t * ptr = packet_cache . ptrw ( ) ;
ptr [ 0 ] = MultiplayerAPI : : NETWORK_COMMAND_SYNC ;
encode_uint64 ( p_scene_id , & ptr [ 1 ] ) ;
Ref < MultiplayerPeer > network_peer = multiplayer - > get_network_peer ( ) ;
network_peer - > set_target_peer ( p_peer_id ) ;
network_peer - > set_transfer_channel ( p_channel ) ;
network_peer - > set_transfer_mode ( p_transfer_mode ) ;
return network_peer - > put_packet ( ptr , SYNC_CMD_OFFSET + p_data . size ( ) ) ;
}
2021-08-09 22:46:23 +02:00
void MultiplayerReplicator : : clear ( ) {
2021-08-12 21:07:44 +02:00
tracked_objects . clear ( ) ;
2021-08-09 22:46:23 +02:00
replicated_nodes . clear ( ) ;
}
void MultiplayerReplicator : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " spawn_config " , " scene_id " , " spawn_mode " , " properties " , " custom_send " , " custom_receive " ) , & MultiplayerReplicator : : spawn_config , DEFVAL ( TypedArray < StringName > ( ) ) , DEFVAL ( Callable ( ) ) , DEFVAL ( Callable ( ) ) ) ;
2021-08-12 21:07:44 +02:00
ClassDB : : bind_method ( D_METHOD ( " sync_config " , " scene_id " , " interval " , " properties " , " custom_send " , " custom_receive " ) , & MultiplayerReplicator : : sync_config , DEFVAL ( TypedArray < StringName > ( ) ) , DEFVAL ( Callable ( ) ) , DEFVAL ( Callable ( ) ) ) ;
2021-08-09 22:46:23 +02:00
ClassDB : : bind_method ( D_METHOD ( " despawn " , " scene_id " , " object " , " peer_id " ) , & MultiplayerReplicator : : despawn , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " spawn " , " scene_id " , " object " , " peer_id " ) , & MultiplayerReplicator : : spawn , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " send_despawn " , " peer_id " , " scene_id " , " data " , " path " ) , & MultiplayerReplicator : : send_despawn , DEFVAL ( Variant ( ) ) , DEFVAL ( NodePath ( ) ) ) ;
ClassDB : : bind_method ( D_METHOD ( " send_spawn " , " peer_id " , " scene_id " , " data " , " path " ) , & MultiplayerReplicator : : send_spawn , DEFVAL ( Variant ( ) ) , DEFVAL ( NodePath ( ) ) ) ;
2021-09-03 19:40:47 +02:00
ClassDB : : bind_method ( D_METHOD ( " send_sync " , " peer_id " , " scene_id " , " data " , " transfer_mode " , " channel " ) , & MultiplayerReplicator : : send_sync , DEFVAL ( Multiplayer : : TRANSFER_MODE_RELIABLE ) , DEFVAL ( 0 ) ) ;
2021-08-12 21:07:44 +02:00
ClassDB : : bind_method ( D_METHOD ( " sync_all " , " scene_id " , " peer_id " ) , & MultiplayerReplicator : : sync_all , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " track " , " scene_id " , " object " ) , & MultiplayerReplicator : : track ) ;
ClassDB : : bind_method ( D_METHOD ( " untrack " , " scene_id " , " object " ) , & MultiplayerReplicator : : untrack ) ;
ClassDB : : bind_method ( D_METHOD ( " encode_state " , " scene_id " , " object " , " initial " ) , & MultiplayerReplicator : : encode_state , DEFVAL ( true ) ) ;
ClassDB : : bind_method ( D_METHOD ( " decode_state " , " scene_id " , " object " , " data " , " initial " ) , & MultiplayerReplicator : : decode_state , DEFVAL ( true ) ) ;
2021-08-09 22:46:23 +02:00
ADD_SIGNAL ( MethodInfo ( " despawned " , PropertyInfo ( Variant : : INT , " scene_id " ) , PropertyInfo ( Variant : : OBJECT , " node " , PROPERTY_HINT_RESOURCE_TYPE , " Node " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " spawned " , PropertyInfo ( Variant : : INT , " scene_id " ) , PropertyInfo ( Variant : : OBJECT , " node " , PROPERTY_HINT_RESOURCE_TYPE , " Node " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " despawn_requested " , PropertyInfo ( Variant : : INT , " id " ) , PropertyInfo ( Variant : : INT , " scene_id " ) , PropertyInfo ( Variant : : OBJECT , " parent " , PROPERTY_HINT_RESOURCE_TYPE , " Node " ) , PropertyInfo ( Variant : : STRING , " name " ) , PropertyInfo ( Variant : : PACKED_BYTE_ARRAY , " data " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " spawn_requested " , PropertyInfo ( Variant : : INT , " id " ) , PropertyInfo ( Variant : : INT , " scene_id " ) , PropertyInfo ( Variant : : OBJECT , " parent " , PROPERTY_HINT_RESOURCE_TYPE , " Node " ) , PropertyInfo ( Variant : : STRING , " name " ) , PropertyInfo ( Variant : : PACKED_BYTE_ARRAY , " data " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " replicated_instance_added " , PropertyInfo ( Variant : : INT , " scene_id " ) , PropertyInfo ( Variant : : OBJECT , " node " , PROPERTY_HINT_RESOURCE_TYPE , " Node " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " replicated_instance_removed " , PropertyInfo ( Variant : : INT , " scene_id " ) , PropertyInfo ( Variant : : OBJECT , " node " , PROPERTY_HINT_RESOURCE_TYPE , " Node " ) ) ) ;
BIND_ENUM_CONSTANT ( REPLICATION_MODE_NONE ) ;
BIND_ENUM_CONSTANT ( REPLICATION_MODE_SERVER ) ;
BIND_ENUM_CONSTANT ( REPLICATION_MODE_CUSTOM ) ;
}