2020-11-28 11:09:25 +01:00
//
// Created by WolverinDEV on 26/11/2020.
//
# include "WhisperHandler.h"
# include "src/client/voice/VoiceClientConnection.h"
# include <misc/endianness.h>
using namespace ts : : server : : whisper ;
constexpr static auto kMaxWhisperTargets { 1024 } ;
WhisperHandler : : WhisperHandler ( SpeakingClient * handle ) : handle { handle } { } ;
WhisperHandler : : ~ WhisperHandler ( ) {
if ( this - > whisper_head_ptr ) {
: : free ( this - > whisper_head_ptr ) ;
}
}
bool WhisperHandler : : validate_whisper_packet ( const protocol : : ClientPacketParser & packet , bool & match_last_header , void * & payload_ptr , size_t & payload_length ) {
size_t head_length ;
if ( packet . flags ( ) & protocol : : PacketFlag : : NewProtocol ) {
if ( packet . payload_length ( ) < 3 + 10 ) {
/* packet too short */
return false ;
}
head_length = 10 ;
} else {
if ( packet . payload_length ( ) < 3 + 2 ) {
/* packet too short */
return false ;
}
auto channel_count = ( uint8_t ) packet . payload ( ) [ 3 ] ;
auto client_count = ( uint8_t ) packet . payload ( ) [ 4 ] ;
head_length = 2 + channel_count * 8 + client_count * 2 ;
if ( packet . payload_length ( ) < 3 + head_length ) {
/* packet is too short */
return false ;
}
}
auto head_ptr = packet . payload ( ) . data_ptr < uint8_t > ( ) + 3 ;
2021-01-14 22:16:56 +01:00
{
std : : lock_guard process_lock { this - > whisper_head_mutex } ;
match_last_header = this - > whisper_head_length = = head_length & & memcmp ( this - > whisper_head_ptr , head_ptr , head_length ) = = 0 ;
if ( ! match_last_header ) {
if ( this - > whisper_head_capacity < head_length ) {
if ( this - > whisper_head_ptr ) {
: : free ( this - > whisper_head_ptr ) ;
}
this - > whisper_head_ptr = malloc ( head_length ) ;
this - > whisper_head_capacity = head_length ;
2020-11-28 11:09:25 +01:00
}
2021-01-14 22:16:56 +01:00
this - > whisper_head_length = head_length ;
memcpy ( this - > whisper_head_ptr , head_ptr , head_length ) ;
2020-11-28 11:09:25 +01:00
}
}
assert ( packet . payload_length ( ) > = head_length + 3 ) ;
payload_ptr = ( void * ) ( head_ptr + head_length ) ;
payload_length = packet . payload_length ( ) - head_length - 3 ;
return true ;
}
bool WhisperHandler : : process_packet ( const protocol : : ClientPacketParser & packet , void * & payload_ptr , size_t & payload_length ) {
bool match_last_header ;
if ( ! this - > validate_whisper_packet ( packet , match_last_header , payload_ptr , payload_length ) ) {
return false ;
}
auto current_timestamp = std : : chrono : : system_clock : : now ( ) ;
switch ( this - > session_state ) {
case SessionState : : Uninitialized :
/* Definitively initialize a new session */
break ;
case SessionState : : InitializeFailed : {
if ( ! match_last_header ) {
/* Last header does not matches the current header, we need to reinitialize the session */
break ;
}
if ( current_timestamp - std : : chrono : : milliseconds { 500 } < this - > session_timestamp ) {
/* Last initialize failed and is less than 500ms ago */
return false ;
}
/* Lets try to initialize the whisper session again */
break ;
}
case SessionState : : Initialized :
if ( ! match_last_header ) {
/* Last header does not matches the current header, we need to reinitialize the session */
break ;
}
if ( current_timestamp - std : : chrono : : seconds { 5 } > this - > session_timestamp ) {
/* Last session member update is 5 seconds ago. Updating the session again */
break ;
}
/* We've nothing to change and everything is good */
return true ;
}
this - > session_timestamp = current_timestamp ;
auto head_ptr = packet . payload ( ) . data_ptr < uint8_t > ( ) ;
size_t head_offset { 3 } ; /* the first three bytes contain the voice sequence id and codec */
if ( packet . flags ( ) & protocol : : PacketFlag : : NewProtocol ) {
auto type = head_ptr [ head_offset + + ] ;
auto target = head_ptr [ head_offset + + ] ;
auto type_id = be2le64 ( head_ptr , head_offset , & head_offset ) ;
auto result = this - > initialize_session_new ( 2 , type , target , type_id ) ;
if ( result . has_error ( ) ) {
this - > session_state = SessionState : : InitializeFailed ;
this - > handle - > notifyError ( result ) ;
} else {
this - > session_state = SessionState : : Initialized ;
}
result . release_data ( ) ;
} else {
auto channel_count = ( uint8_t ) head_ptr [ head_offset + + ] ;
auto client_count = ( uint8_t ) head_ptr [ head_offset + + ] ;
ChannelId channel_ids [ channel_count ] ;
ClientId client_ids [ client_count ] ;
for ( uint8_t index = 0 ; index < channel_count ; index + + ) {
channel_ids [ index ] = be2le64 ( head_ptr , head_offset , & head_offset ) ;
}
for ( uint8_t index = 0 ; index < client_count ; index + + ) {
client_ids [ index ] = be2le16 ( head_ptr , head_offset , & head_offset ) ;
}
auto result = this - > initialize_session_old ( 2 , client_ids , client_count , channel_ids , channel_count ) ;
if ( result . has_error ( ) ) {
this - > session_state = SessionState : : InitializeFailed ;
this - > handle - > notifyError ( result ) ;
} else {
this - > session_state = SessionState : : Initialized ;
}
result . release_data ( ) ;
}
return this - > session_state = = SessionState : : Initialized ;
}
2020-11-28 11:27:31 +01:00
void WhisperHandler : : signal_session_reset ( ) {
auto server = this - > handle - > getServer ( ) ;
if ( ! server ) {
return ;
}
server - > rtc_server ( ) . reset_whisper_session ( this - > handle - > rtc_client_id ) ;
}
2020-11-28 11:09:25 +01:00
void WhisperHandler : : handle_session_reset ( ) {
2020-12-19 10:52:24 +01:00
std : : lock_guard process_lock { this - > whisper_head_mutex } ;
2020-11-28 11:09:25 +01:00
this - > session_state = SessionState : : Uninitialized ;
if ( this - > whisper_head_ptr ) {
: : free ( this - > whisper_head_ptr ) ;
this - > whisper_head_capacity = 0 ;
this - > whisper_head_length = 0 ;
}
}
size_t WhisperHandler : : max_whisper_targets ( ) {
auto server = this - > handle - > getServer ( ) ;
if ( ! server ) {
return false ;
}
auto result = server - > properties ( ) [ property : : VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE ] . as_save < size_t > ( [ ] { return kMaxWhisperTargets ; } ) ;
if ( result > kMaxWhisperTargets ) {
result = kMaxWhisperTargets ;
}
return result ;
}
ts : : command_result WhisperHandler : : initialize_session_old ( uint32_t stream_id , const uint16_t * client_ids , size_t client_count , const uint64_t * channel_ids , size_t channel_count ) {
auto server = this - > handle - > getServer ( ) ;
if ( ! server ) {
return ts : : command_result { error : : vs_critical , " missing server " } ;
}
std : : vector < std : : shared_ptr < SpeakingClient > > target_clients { } ;
auto connected_clients = server - > getClients ( ) ;
target_clients . reserve ( connected_clients . size ( ) ) ;
for ( const auto & connected_client : connected_clients ) {
auto speaking_client = dynamic_pointer_cast < SpeakingClient > ( connected_client ) ;
if ( ! speaking_client | | speaking_client = = this - > handle | | ! speaking_client - > rtc_client_id ) {
continue ;
}
auto client_channel_id = speaking_client - > getChannelId ( ) ;
auto client_id = speaking_client - > getClientId ( ) ;
for ( size_t index = 0 ; index < channel_count ; index + + ) {
if ( channel_ids [ index ] = = client_channel_id ) {
goto add_client ;
}
}
for ( size_t index = 0 ; index < client_count ; index + + ) {
if ( client_ids [ index ] = = client_id ) {
goto add_client ;
}
}
continue ;
add_client :
target_clients . push_back ( speaking_client ) ;
}
return this - > configure_rtc_clients ( stream_id , target_clients ) ;
}
ts : : command_result WhisperHandler : : initialize_session_new ( uint32_t stream_id , uint8_t type_u8 , uint8_t target_u8 , uint64_t type_id ) {
auto type = ( WhisperType ) type_u8 ;
auto target = ( WhisperTarget ) target_u8 ;
auto server = this - > handle - > getServer ( ) ;
if ( ! server ) {
return ts : : command_result { error : : vs_critical , " missing server " } ;
}
# ifdef PKT_LOG_WHISPER
logTrace ( this - > getServerId ( ) , " {} Whisper data length: {}. Type: {}. Target: {}. Target ID: {}. " , CLIENT_STR_LOG_PREFIX , data_length , type , target , type_id ) ;
# endif
std : : vector < std : : shared_ptr < SpeakingClient > > target_clients { } ;
if ( type = = WhisperType : : ECHO ) {
target_clients . push_back ( dynamic_pointer_cast < SpeakingClient > ( this - > handle - > ref ( ) ) ) ;
} else {
auto connected_clients = server - > getClients ( ) ;
target_clients . reserve ( connected_clients . size ( ) ) ;
for ( const auto & available_client : connected_clients ) {
auto speaking_client = dynamic_pointer_cast < SpeakingClient > ( available_client ) ;
if ( ! speaking_client | | this - > handle = = speaking_client | | ! speaking_client - > rtc_client_id ) {
continue ;
}
if ( type = = WhisperType : : ALL ) {
target_clients . push_back ( speaking_client ) ;
} else if ( type = = WhisperType : : SERVER_GROUP ) {
if ( type_id = = 0 ) {
target_clients . push_back ( speaking_client ) ;
} else {
std : : shared_lock client_view_lock ( speaking_client - > get_channel_lock ( ) ) ;
for ( const auto & id : speaking_client - > current_server_groups ( ) ) {
if ( id = = type_id ) {
target_clients . push_back ( speaking_client ) ;
break ;
}
}
}
} else if ( type = = WhisperType : : CHANNEL_GROUP ) {
if ( speaking_client - > current_channel_group ( ) = = type_id ) {
target_clients . push_back ( speaking_client ) ;
}
} else if ( type = = WhisperType : : CHANNEL_COMMANDER ) {
if ( speaking_client - > properties ( ) [ property : : CLIENT_IS_CHANNEL_COMMANDER ] . as_save < bool > ( [ ] { return false ; } ) ) {
target_clients . push_back ( speaking_client ) ;
}
} else {
return ts : : command_result { error : : parameter_invalid , " type " } ;
}
}
if ( target = = WhisperTarget : : CHANNEL_CURRENT ) {
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
return target - > getChannel ( ) ! = this - > handle - > getChannel ( ) ;
} ) ,
target_clients . end ( ) ) ;
} else if ( target = = WhisperTarget : : CHANNEL_PARENT ) {
auto current_parent = this - > handle - > getChannel ( ) ;
if ( current_parent & & ( current_parent = current_parent - > parent ( ) ) ) {
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
return target - > getChannel ( ) ! = current_parent ;
} ) ,
target_clients . end ( ) ) ;
} else {
target_clients . clear ( ) ;
}
} else if ( target = = WhisperTarget : : CHANNEL_ALL_PARENT ) {
auto current_parent = this - > handle - > getChannel ( ) ;
if ( current_parent & & ( current_parent = current_parent - > parent ( ) ) ) {
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
auto tmp_parent = current_parent ;
while ( tmp_parent & & tmp_parent ! = target - > getChannel ( ) )
tmp_parent = tmp_parent - > parent ( ) ;
return target - > getChannel ( ) ! = tmp_parent ;
} ) ,
target_clients . end ( ) ) ;
} else {
target_clients . clear ( ) ;
}
} else if ( target = = WhisperTarget : : CHANNEL_FAMILY ) {
auto current_channel = this - > handle - > getChannel ( ) ;
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
auto tmp_current = target - > getChannel ( ) ;
while ( tmp_current & & tmp_current ! = current_channel ) {
tmp_current = tmp_current - > parent ( ) ;
}
return current_channel ! = tmp_current ;
} ) ,
target_clients . end ( ) ) ;
} else if ( target = = WhisperTarget : : CHANNEL_COMPLETE_FAMILY ) {
shared_ptr < BasicChannel > current = this - > handle - > getChannel ( ) ;
while ( current & & current - > parent ( ) ) current = current - > parent ( ) ;
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
auto tmp_current = target - > getChannel ( ) ;
while ( tmp_current & & tmp_current ! = current )
tmp_current = tmp_current - > parent ( ) ;
return current ! = tmp_current ;
} ) ,
target_clients . end ( ) ) ;
} else if ( target = = WhisperTarget : : CHANNEL_SUBCHANNELS ) {
shared_ptr < BasicChannel > current = this - > handle - > getChannel ( ) ;
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
return target - > getChannel ( ) - > parent ( ) ! = current ;
} ) ,
target_clients . end ( ) ) ;
} else if ( target = = WhisperTarget : : CHANNEL_ALL ) {
/* nothing to filter out */
} else {
return ts : : command_result { error : : parameter_invalid , " target " } ;
}
}
return this - > configure_rtc_clients ( stream_id , target_clients ) ;
}
ts : : command_result WhisperHandler : : configure_rtc_clients ( uint32_t stream_id , const std : : vector < std : : shared_ptr < SpeakingClient > > & target_clients ) {
auto max_clients = this - > max_whisper_targets ( ) ;
assert ( max_clients < = kMaxWhisperTargets ) ;
if ( target_clients . size ( ) > = max_clients ) {
return ts : : command_result { error : : whisper_too_many_targets } ;
}
if ( target_clients . empty ( ) ) {
return ts : : command_result { error : : whisper_no_targets } ;
}
auto server = this - > handle - > getServer ( ) ;
if ( ! server ) {
return ts : : command_result { error : : vs_critical , " missing server " } ;
}
uint32_t target_client_ids [ kMaxWhisperTargets ] ;
size_t target_client_count { 0 } ;
for ( const auto & target_client : target_clients ) {
target_client_ids [ target_client_count + + ] = target_client - > rtc_client_id ;
}
std : : string error ;
if ( ! server - > rtc_server ( ) . configure_whisper_session ( error , this - > handle - > rtc_client_id , stream_id , target_client_ids , target_client_count ) ) {
logCritical ( server - > getServerId ( ) , " {} Failed to configure whisper session with {} participants. " , CLIENT_STR_LOG_PREFIX_ ( this - > handle ) , target_client_count ) ;
return ts : : command_result { error : : vs_critical , error } ;
}
logTrace ( server - > getServerId ( ) , " {} Configured whisper session with {} participants. " , CLIENT_STR_LOG_PREFIX_ ( this - > handle ) , target_client_count ) ;
return ts : : command_result { error : : ok } ;
}