2019-07-17 19:37:18 +02:00
# include <algorithm>
# include <memory>
# include <tomcrypt.h>
# include <arpa/inet.h>
# include <ThreadPool/Timer.h>
# include <misc/endianness.h>
# include <misc/memtracker.h>
# include <log/LogUtils.h>
# include "VoiceClient.h"
2020-06-28 14:01:14 +02:00
# include "src/InstanceHandler.h"
# include "src/manager/ActionLogger.h"
2019-07-17 19:37:18 +02:00
using namespace std ;
using namespace std : : chrono ;
using namespace ts : : server ;
using namespace ts : : protocol ;
VoiceClient : : VoiceClient ( const std : : shared_ptr < VoiceServer > & server , const sockaddr_storage * address ) : SpeakingClient ( server - > server - > sql , server - > server ) , voice_server ( server ) {
2020-01-24 02:57:58 +01:00
assert ( address ) ;
memtrack : : allocated < VoiceClient > ( this ) ;
memcpy ( & this - > remote_address , address , sizeof ( sockaddr_storage ) ) ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
debugMessage ( this - > server - > getServerId ( ) , " Creating VoiceClient instance at {} " , ( void * ) this ) ;
2019-07-17 19:37:18 +02:00
}
void VoiceClient : : initialize ( ) {
2020-01-24 02:57:58 +01:00
this - > event_handle_packet = make_shared < event : : ProxiedEventEntry < VoiceClient > > ( dynamic_pointer_cast < VoiceClient > ( this - > ref ( ) ) , & VoiceClient : : execute_handle_packet ) ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
this - > properties ( ) [ property : : CLIENT_TYPE ] = ClientType : : CLIENT_TEAMSPEAK ;
this - > properties ( ) [ property : : CLIENT_TYPE_EXACT ] = ClientType : : CLIENT_TEAMSPEAK ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
this - > state = ConnectionState : : INIT_HIGH ;
this - > connection = new connection : : VoiceClientConnection ( this ) ;
2019-07-17 19:37:18 +02:00
}
VoiceClient : : ~ VoiceClient ( ) {
2020-01-24 02:57:58 +01:00
debugMessage ( this - > getServerId ( ) , " Deleting VoiceClient instance at {} " , ( void * ) this ) ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
this - > state = ConnectionState : : DISCONNECTED ;
delete this - > connection ;
this - > connection = nullptr ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
if ( this - > flushing_thread )
logCritical ( this - > getServerId ( ) , " Deleting a VoiceClient which should still be hold within the flush thread! " ) ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
memtrack : : freed < VoiceClient > ( this ) ;
2019-07-17 19:37:18 +02:00
}
2020-04-24 22:04:07 +02:00
void VoiceClient : : sendCommand0 ( const std : : string_view & cmd , bool low , std : : unique_ptr < threads : : Future < bool > > listener ) {
this - > connection - > send_command ( cmd , low , std : : move ( listener ) ) ;
2019-07-17 19:37:18 +02:00
# ifdef PKT_LOG_CMD
2020-01-26 14:21:34 +01:00
logTrace ( this - > getServerId ( ) , " {}[Command][Server -> Client] Sending command {}. Command low: {}. Full command: {} " , CLIENT_STR_LOG_PREFIX , cmd . substr ( 0 , cmd . find ( ' ' ) ) , low , cmd ) ;
2019-07-17 19:37:18 +02:00
# endif
}
2020-07-29 19:05:38 +02:00
2019-07-17 19:37:18 +02:00
void VoiceClient : : tick ( const std : : chrono : : system_clock : : time_point & time ) {
SpeakingClient : : tick ( time ) ;
2020-01-24 02:57:58 +01:00
{
ALARM_TIMER ( A1 , " VoiceClient::tick " , milliseconds ( 3 ) ) ;
if ( this - > state = = ConnectionState : : CONNECTED ) {
2020-07-29 22:53:40 +02:00
this - > connection - > ping_handler ( ) . tick ( time ) ;
2020-04-08 13:01:41 +02:00
this - > connection - > packet_statistics ( ) . tick ( ) ;
2020-01-24 02:57:58 +01:00
} else if ( this - > state = = ConnectionState : : INIT_LOW | | this - > state = = ConnectionState : : INIT_HIGH ) {
2020-07-29 22:53:40 +02:00
auto last_command = this - > connection - > crypt_setup_handler ( ) . last_handled_command ( ) ;
if ( last_command . time_since_epoch ( ) . count ( ) ! = 0 ) {
if ( time - last_command > seconds ( 5 ) ) {
2020-01-24 02:57:58 +01:00
debugMessage ( this - > getServerId ( ) , " {} Got handshake timeout. {}. State: {} Time: {} " , CLIENT_STR_LOG_PREFIX ,
this - > getLoggingPeerIp ( ) + " : " + to_string ( this - > getPeerPort ( ) ) ,
this - > state = = ConnectionState : : INIT_HIGH ? " INIT_HIGH " : " INIT_LOW " ,
2020-07-29 22:53:40 +02:00
duration_cast < seconds > ( time - last_command ) . count ( )
2020-01-24 02:57:58 +01:00
) ;
2020-02-01 14:32:16 +01:00
this - > close_connection ( system_clock : : now ( ) + seconds ( 1 ) ) ;
2020-01-24 02:57:58 +01:00
}
}
}
}
2019-07-17 19:37:18 +02:00
}
2020-07-29 22:53:40 +02:00
std : : chrono : : milliseconds VoiceClient : : current_ping ( ) {
return this - > connection - > ping_handler ( ) . current_ping ( ) ;
}
2019-07-17 19:37:18 +02:00
bool VoiceClient : : disconnect ( const std : : string & reason ) {
2020-01-24 02:57:58 +01:00
return this - > disconnect ( VREASON_SERVER_KICK , reason , this - > server - > serverRoot , true ) ;
2019-07-17 19:37:18 +02:00
}
bool VoiceClient : : disconnect ( ts : : ViewReasonId reason_id , const std : : string & reason , const std : : shared_ptr < ts : : server : : ConnectedClient > & invoker , bool notify_viewer ) {
2020-02-01 14:32:16 +01:00
/*
* We don ' t have to lock the disconnect lock here , because we ' re not really unregistering the client .
* Its only for the clients own flavour and everything which the client receives after will be ignored : )
*/
ConnectionState old_state { } ;
2020-01-24 02:57:58 +01:00
{
2020-02-01 14:32:16 +01:00
std : : lock_guard state_lock { this - > state_lock } ;
if ( this - > state = = ConnectionState : : DISCONNECTING | | this - > state = = ConnectionState : : DISCONNECTED )
return false ; //Already disconnecting/disconnected
old_state = this - > state ;
2020-01-24 02:57:58 +01:00
this - > state = ConnectionState : : DISCONNECTING ;
}
2020-02-01 14:32:16 +01:00
if ( old_state = = ConnectionState : : CONNECTED ) {
/* Client has been successflly initialized; Send normal disconnect. */
Command cmd ( " notifyclientleftview " ) ;
cmd [ " reasonmsg " ] = reason ;
cmd [ " reasonid " ] = reason_id ;
cmd [ " clid " ] = this - > getClientId ( ) ;
cmd [ " cfid " ] = this - > currentChannel ? this - > currentChannel - > channelId ( ) : 0 ; //Failed when cid = 0????
cmd [ " ctid " ] = 0 ;
2020-01-24 02:57:58 +01:00
2020-02-01 14:32:16 +01:00
if ( invoker ) {
cmd [ " invokerid " ] = invoker - > getClientId ( ) ;
cmd [ " invokername " ] = invoker - > getDisplayName ( ) ;
cmd [ " invokeruid " ] = invoker - > getUid ( ) ;
}
2020-06-28 14:01:14 +02:00
auto old_channel = this - > currentChannel ;
if ( old_channel ) {
serverInstance - > action_logger ( ) - > client_channel_logger . log_client_leave ( this - > getServerId ( ) , this - > ref ( ) , old_channel - > channelId ( ) , old_channel - > name ( ) ) ;
}
2020-02-01 14:32:16 +01:00
if ( notify_viewer & & this - > server ) {
unique_lock channel_lock ( this - > server - > channel_tree_lock ) ;
this - > server - > client_move ( this - > ref ( ) , nullptr , invoker , reason , reason_id , false , channel_lock ) ;
} else {
threads : : MutexLock lock ( this - > command_lock ) ;
auto server_channel = dynamic_pointer_cast < ServerChannel > ( this - > currentChannel ) ;
if ( server_channel )
server_channel - > unregister_client ( _this . lock ( ) ) ;
this - > currentChannel = nullptr ;
}
auto listener = make_unique < threads : : Future < bool > > ( ) ;
auto weak_self = this - > _this ;
listener - > waitAndGetLater ( [ weak_self ] ( bool * success ) {
if ( weak_self . expired ( ) ) return ;
auto self = weak_self . lock ( ) ;
if ( ! self ) return ;
if ( ! success | | ! * success ) {
debugMessage ( self - > getServerId ( ) , " {} Failed to receive disconnect acknowledge! " , CLIENT_STR_LOG_PREFIX_ ( self ) ) ;
} else
debugMessage ( self - > getServerId ( ) , " {} Received disconnect acknowledge! " , CLIENT_STR_LOG_PREFIX_ ( self ) ) ;
self - > close_connection ( chrono : : system_clock : : time_point { } ) ; /* we received the ack, we do not need to flush anything */
} , system_clock : : now ( ) + seconds ( 5 ) ) ;
2020-04-24 22:04:07 +02:00
this - > sendCommand0 ( cmd . build ( ) , false , std : : move ( listener ) ) ;
2020-01-24 02:57:58 +01:00
} else {
2020-02-01 14:32:16 +01:00
//TODO: Extra case for INIT_HIGH?
this - > close_connection ( chrono : : system_clock : : now ( ) + chrono : : seconds { 5 } ) ;
2020-01-24 02:57:58 +01:00
}
return true ;
2019-07-17 19:37:18 +02:00
}
2020-02-01 14:32:16 +01:00
bool VoiceClient : : close_connection ( const system_clock : : time_point & timeout ) {
2020-01-24 02:57:58 +01:00
auto self_lock = dynamic_pointer_cast < VoiceClient > ( _this . lock ( ) ) ;
assert ( self_lock ) ; //Should never happen!
bool flush = timeout . time_since_epoch ( ) . count ( ) > 0 ;
2020-02-01 14:32:16 +01:00
{
std : : lock_guard state_lock { this - > state_lock } ;
if ( this - > state = = ConnectionState : : DISCONNECTED ) return false ;
else if ( this - > state = = ConnectionState : : DISCONNECTING ) {
/* here is nothing to pay attention for */
} else if ( this - > state = = ConnectionState : : DISCONNECTING_FLUSHING ) {
if ( ! flush ) {
this - > state = ConnectionState : : DISCONNECTED ;
return true ; /* the flush thread will execute the final disconnect */
} else {
//TODO: May update the flush timeout if its less then the other one?
return true ;
}
}
this - > state = flush ? ConnectionState : : DISCONNECTING_FLUSHING : ConnectionState : : DISCONNECTED ;
2020-01-24 02:57:58 +01:00
}
debugMessage ( this - > getServerId ( ) , " {} Closing voice client connection. (Flush: {}) " , CLIENT_STR_LOG_PREFIX , flush ) ;
2020-02-01 14:32:16 +01:00
//TODO: Move this out into a thread pool?
this - > flushing_thread = std : : make_shared < threads : : Thread > ( THREAD_SAVE_OPERATIONS | THREAD_EXECUTE_LATER , [ this , self_lock , timeout , flush ] {
{
/* Await that all commands have been processed. It does not make sense to unregister the client while command handling. */
std : : lock_guard cmd_lock { this - > command_lock } ;
}
2020-01-24 02:57:58 +01:00
2020-02-01 14:32:16 +01:00
if ( flush ) {
debugMessage ( this - > getServerId ( ) , " {} Awaiting write prepare, write and acknowledge queue flushed " , CLIENT_STR_LOG_PREFIX ) ;
while ( this - > state = = DISCONNECTING_FLUSHING ) {
2020-01-24 02:57:58 +01:00
if ( system_clock : : now ( ) > timeout ) {
2020-02-01 14:32:16 +01:00
auto write_queue_flushed = this - > connection - > wait_empty_write_and_prepare_queue ( timeout ) ;
2020-07-29 19:05:38 +02:00
auto acknowledge_received = connection - > packet_encoder ( ) . acknowledge_manager ( ) . awaiting_acknowledge ( ) = = 0 ;
2020-01-24 02:57:58 +01:00
2020-02-01 14:32:16 +01:00
if ( write_queue_flushed & & acknowledge_received )
break ;
debugMessage ( this - > getServerId ( ) , " {} Failed to flush pending messages. Acknowledges pending: {} Buffers pending: {} " , CLIENT_STR_LOG_PREFIX , acknowledge_received , write_queue_flushed ) ;
2020-01-24 02:57:58 +01:00
break ;
}
if ( ! this - > connection - > wait_empty_write_and_prepare_queue ( timeout ) )
continue ;
2020-07-29 19:05:38 +02:00
if ( connection - > packet_encoder ( ) . acknowledge_manager ( ) . awaiting_acknowledge ( ) > 0 ) {
2020-02-01 14:32:16 +01:00
usleep ( 5000 ) ;
continue ;
2020-01-24 02:57:58 +01:00
}
debugMessage ( this - > getServerId ( ) , " {} Write and acknowledge queue are flushed " , CLIENT_STR_LOG_PREFIX ) ;
break ;
}
}
2020-02-01 14:32:16 +01:00
if ( this - > state > DISCONNECTING ) /* it could happen that the client "reconnects" while flushing this shit */
this - > finalDisconnect ( ) ;
} ) ;
flushing_thread - > name ( " Flush thread VC " ) . execute ( ) ;
2020-01-24 02:57:58 +01:00
return true ;
2019-07-17 19:37:18 +02:00
}
void VoiceClient : : finalDisconnect ( ) {
2020-01-24 02:57:58 +01:00
auto ownLock = dynamic_pointer_cast < VoiceClient > ( _this . lock ( ) ) ;
assert ( ownLock ) ;
lock_guard disconnect_lock_final ( this - > finalDisconnectLock ) ;
if ( this - > final_disconnected ) {
logError ( this - > getServerId ( ) , " Tried to final disconnect {}/{} twice " , this - > getLoggingPeerIp ( ) + " : " + to_string ( this - > getPeerPort ( ) ) , this - > getDisplayName ( ) ) ;
return ;
}
this - > final_disconnected = true ;
this - > state = ConnectionState : : DISCONNECTED ;
threads : : MutexLock command_lock ( this - > command_lock ) ; //We should not progress any commands while disconnecting
//Unload manager cache
this - > processLeave ( ) ;
{
if ( this - > flushing_thread ) this - > flushing_thread - > detach ( ) ; //The thread itself should be already done or executing this method
this - > flushing_thread . reset ( ) ;
}
if ( this - > voice_server ) this - > voice_server - > unregisterConnection ( ownLock ) ;
2019-07-17 19:37:18 +02:00
}
void VoiceClient : : execute_handle_packet ( const std : : chrono : : system_clock : : time_point & time ) {
2020-07-29 22:53:40 +02:00
this - > server_command_executor_ . execute_handle_command_packets ( time ) ;
2019-07-17 19:37:18 +02:00
}
void VoiceClient : : send_voice_packet ( const pipes : : buffer_view & voice_buffer , const SpeakingClient : : VoicePacketFlags & flags ) {
2020-04-24 22:04:07 +02:00
PacketFlag : : PacketFlags packet_flags { PacketFlag : : None } ;
packet_flags | = flags . encrypted ? 0U : PacketFlag : : Unencrypted ;
packet_flags | = flags . head ? PacketFlag : : Compressed : 0U ;
packet_flags | = flags . fragmented ? PacketFlag : : Fragmented : 0U ;
packet_flags | = flags . new_protocol ? PacketFlag : : NewProtocol : 0U ;
2020-01-24 02:57:58 +01:00
2020-04-24 22:04:07 +02:00
this - > connection - > send_packet ( PacketType : : VOICE , packet_flags , voice_buffer . data_ptr < void > ( ) , voice_buffer . length ( ) ) ;
2019-07-17 19:37:18 +02:00
}
2020-09-06 21:00:27 +02:00
void VoiceClient : : send_voice_whisper_packet ( const pipes : : buffer_view & teamspeak_packet , const pipes : : buffer_view & teaspeak_packet , const SpeakingClient : : VoicePacketFlags & flags ) {
2020-04-24 22:04:07 +02:00
PacketFlag : : PacketFlags packet_flags { PacketFlag : : None } ;
packet_flags | = flags . encrypted ? 0U : PacketFlag : : Unencrypted ;
packet_flags | = flags . head ? PacketFlag : : Compressed : 0U ;
packet_flags | = flags . fragmented ? PacketFlag : : Fragmented : 0U ;
packet_flags | = flags . new_protocol ? PacketFlag : : NewProtocol : 0U ;
2020-01-24 02:57:58 +01:00
2020-09-06 21:00:27 +02:00
if ( this - > getType ( ) = = ClientType : : CLIENT_TEASPEAK ) {
this - > connection - > send_packet ( PacketType : : VOICE_WHISPER , packet_flags , teaspeak_packet . data_ptr < void > ( ) , teaspeak_packet . length ( ) ) ;
} else {
this - > connection - > send_packet ( PacketType : : VOICE_WHISPER , packet_flags , teamspeak_packet . data_ptr < void > ( ) , teamspeak_packet . length ( ) ) ;
}
2020-04-08 13:01:41 +02:00
}
float VoiceClient : : current_ping_deviation ( ) {
2020-07-29 19:05:38 +02:00
return this - > connection - > packet_encoder ( ) . acknowledge_manager ( ) . current_rttvar ( ) ;
2020-04-08 13:01:41 +02:00
}
float VoiceClient : : current_packet_loss ( ) const {
return this - > connection - > packet_statistics ( ) . current_packet_loss ( ) ;
2020-11-07 13:17:51 +01:00
}
void VoiceClient : : processJoin ( ) {
SpeakingClient : : processJoin ( ) ;
if ( this - > rtc_client_id > 0 ) {
auto sender = this - > server - > rtc_server ( ) . create_audio_source_supplier_sender ( this - > rtc_client_id ) ;
assert ( sender . has_value ( ) ) ;
this - > rtc_audio_supplier . reset ( * sender ) ;
}
}