2019-07-17 13:37:18 -04:00
# include <algorithm>
# include <memory>
# include <tomcrypt.h>
# include <arpa/inet.h>
# include <ThreadPool/Timer.h>
# include <misc/memtracker.h>
# include <log/LogUtils.h>
# include "VoiceClient.h"
2020-06-28 08:01:14 -04:00
# include "src/InstanceHandler.h"
# include "src/manager/ActionLogger.h"
2019-07-17 13:37:18 -04:00
using namespace std ;
using namespace std : : chrono ;
using namespace ts : : server ;
using namespace ts : : protocol ;
2020-11-28 05:09:25 -05:00
constexpr static auto kMaxWhisperClientNameLength { 30 } ;
constexpr static auto kWhisperClientUniqueIdLength { 28 } ; /* base64 encoded SHA1 hash */
2021-01-28 14:59:15 -05:00
VoiceClient : : VoiceClient ( const std : : shared_ptr < VoiceServer > & server , const sockaddr_storage * address ) :
2021-04-15 11:34:44 -04:00
SpeakingClient { server - > get_server ( ) - > sql , server - > get_server ( ) } ,
2021-01-28 14:59:15 -05:00
voice_server ( server ) {
2020-01-23 20:57:58 -05:00
assert ( address ) ;
memtrack : : allocated < VoiceClient > ( this ) ;
memcpy ( & this - > remote_address , address , sizeof ( sockaddr_storage ) ) ;
2019-07-17 13:37:18 -04:00
2020-01-23 20:57:58 -05:00
debugMessage ( this - > server - > getServerId ( ) , " Creating VoiceClient instance at {} " , ( void * ) this ) ;
2019-07-17 13:37:18 -04:00
}
void VoiceClient : : initialize ( ) {
2021-01-28 14:59:15 -05:00
auto ref_self = dynamic_pointer_cast < VoiceClient > ( this - > ref ( ) ) ;
2021-04-15 11:14:47 -04:00
assert ( ref_self ) ;
this - > ref_self_voice = ref_self ;
2021-01-28 14:59:15 -05:00
this - > server_command_queue_ = std : : make_unique < ServerCommandQueue > (
serverInstance - > server_command_executor ( ) ,
std : : make_unique < VoiceClientCommandHandler > ( ref_self )
) ;
2019-07-17 13:37:18 -04:00
2020-01-23 20:57:58 -05:00
this - > properties ( ) [ property : : CLIENT_TYPE ] = ClientType : : CLIENT_TEAMSPEAK ;
this - > properties ( ) [ property : : CLIENT_TYPE_EXACT ] = ClientType : : CLIENT_TEAMSPEAK ;
2019-07-17 13:37:18 -04:00
2020-01-23 20:57:58 -05:00
this - > state = ConnectionState : : INIT_HIGH ;
this - > connection = new connection : : VoiceClientConnection ( this ) ;
2019-07-17 13:37:18 -04:00
}
VoiceClient : : ~ VoiceClient ( ) {
2020-01-23 20:57:58 -05:00
debugMessage ( this - > getServerId ( ) , " Deleting VoiceClient instance at {} " , ( void * ) this ) ;
2019-07-17 13:37:18 -04:00
2020-01-23 20:57:58 -05:00
this - > state = ConnectionState : : DISCONNECTED ;
delete this - > connection ;
this - > connection = nullptr ;
2019-07-17 13:37:18 -04:00
2021-04-14 21:28:08 -04:00
if ( this - > flush_task ) {
logCritical ( this - > getServerId ( ) , " VoiceClient deleted with an active pending flush task! " ) ;
serverInstance - > general_task_executor ( ) - > cancel_task ( this - > flush_task ) ;
2020-12-05 10:27:41 -05:00
}
2019-07-17 13:37:18 -04:00
2020-01-23 20:57:58 -05:00
memtrack : : freed < VoiceClient > ( this ) ;
2019-07-17 13:37:18 -04:00
}
2021-02-05 08:20:11 -05:00
void VoiceClient : : sendCommand0 ( const std : : string_view & cmd , bool low , std : : unique_ptr < std : : function < void ( bool ) > > listener ) {
2020-04-24 16:04:07 -04:00
this - > connection - > send_command ( cmd , low , std : : move ( listener ) ) ;
2019-07-17 13:37:18 -04:00
# ifdef PKT_LOG_CMD
2020-01-26 08:21:34 -05: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 13:37:18 -04:00
# endif
}
2020-07-29 13:05:38 -04:00
2021-01-28 14:59:15 -05:00
void VoiceClient : : tick_server ( const std : : chrono : : system_clock : : time_point & time ) {
SpeakingClient : : tick_server ( time ) ;
2020-01-23 20:57:58 -05:00
{
ALARM_TIMER ( A1 , " VoiceClient::tick " , milliseconds ( 3 ) ) ;
if ( this - > state = = ConnectionState : : CONNECTED ) {
2020-07-29 16:53:40 -04:00
this - > connection - > ping_handler ( ) . tick ( time ) ;
2020-04-08 07:01:41 -04:00
this - > connection - > packet_statistics ( ) . tick ( ) ;
2020-01-23 20:57:58 -05:00
} else if ( this - > state = = ConnectionState : : INIT_LOW | | this - > state = = ConnectionState : : INIT_HIGH ) {
2020-07-29 16:53:40 -04: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-23 20:57:58 -05: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 16:53:40 -04:00
duration_cast < seconds > ( time - last_command ) . count ( )
2020-01-23 20:57:58 -05:00
) ;
2020-02-01 08:32:16 -05:00
this - > close_connection ( system_clock : : now ( ) + seconds ( 1 ) ) ;
2020-01-23 20:57:58 -05:00
}
}
}
}
2019-07-17 13:37:18 -04:00
}
2020-07-29 16:53:40 -04:00
std : : chrono : : milliseconds VoiceClient : : current_ping ( ) {
return this - > connection - > ping_handler ( ) . current_ping ( ) ;
}
2019-07-17 13:37:18 -04:00
bool VoiceClient : : disconnect ( const std : : string & reason ) {
2020-01-23 20:57:58 -05:00
return this - > disconnect ( VREASON_SERVER_KICK , reason , this - > server - > serverRoot , true ) ;
2019-07-17 13:37:18 -04:00
}
bool VoiceClient : : disconnect ( ts : : ViewReasonId reason_id , const std : : string & reason , const std : : shared_ptr < ts : : server : : ConnectedClient > & invoker , bool notify_viewer ) {
2021-04-14 21:28:08 -04:00
task_id disconnect_id ;
auto weak_client { this - > weak_ref ( ) } ;
serverInstance - > general_task_executor ( ) - > schedule ( disconnect_id , " voice client disconnect of " + this - > getLoggingPrefix ( ) , [
weak_client ,
reason_id ,
reason ,
invoker ,
notify_viewer
] {
auto client = dynamic_pointer_cast < VoiceClient > ( weak_client . lock ( ) ) ;
if ( ! client ) {
/* client has already been deallocated */
return ;
}
2020-02-01 08:32:16 -05:00
2021-04-14 21:28:08 -04:00
bool await_disconnect_ack { false } ;
{
std : : lock_guard state_lock { client - > state_lock } ;
switch ( client - > state ) {
case ConnectionState : : DISCONNECTING :
case ConnectionState : : DISCONNECTED :
/* somebody else already disconnected the client */
return ;
case ConnectionState : : INIT_HIGH :
case ConnectionState : : INIT_LOW :
/* disconnect the client and close the connection */
break ;
2020-01-23 20:57:58 -05:00
2021-04-14 21:28:08 -04:00
case ConnectionState : : CONNECTED :
await_disconnect_ack = true ;
break ;
2020-02-01 08:32:16 -05:00
2021-04-14 21:28:08 -04:00
case ConnectionState : : UNKNWON :
default :
assert ( false ) ;
return ;
}
2020-01-23 20:57:58 -05:00
2021-04-14 21:28:08 -04:00
client - > state = ConnectionState : : DISCONNECTING ;
2020-02-01 08:32:16 -05:00
}
2021-04-14 21:28:08 -04:00
/* server should never be a nullptr */
assert ( client - > server ) ;
2020-06-28 08:01:14 -04:00
2021-04-14 21:28:08 -04:00
ChannelId client_channel { client - > getChannelId ( ) } ;
{
std : : lock_guard command_lock { client - > command_lock } ;
std : : unique_lock server_tree_lock { client - > server - > channel_tree_mutex } ;
if ( client - > currentChannel ) {
if ( notify_viewer ) {
client - > server - > client_move ( client - > ref ( ) , nullptr , invoker , reason , reason_id , false , server_tree_lock ) ;
} else {
auto server_channel = dynamic_pointer_cast < ServerChannel > ( client - > currentChannel ) ;
assert ( server_channel ) ;
server_channel - > unregister_client ( client ) ;
client - > currentChannel = nullptr ;
}
}
2020-02-01 08:32:16 -05:00
}
2021-04-14 21:28:08 -04:00
{
ts : : command_builder notify { " notifyclientleftview " } ;
notify . put_unchecked ( 0 , " reasonmsg " , reason ) ;
notify . put_unchecked ( 0 , " reasonid " , reason_id ) ;
notify . put_unchecked ( 0 , " clid " , client - > getClientId ( ) ) ;
notify . put_unchecked ( 0 , " cfid " , client_channel ) ;
notify . put_unchecked ( 0 , " ctid " , " 0 " ) ;
if ( invoker ) {
notify . put_unchecked ( 0 , " invokerid " , invoker - > getClientId ( ) ) ;
notify . put_unchecked ( 0 , " invokername " , invoker - > getDisplayName ( ) ) ;
notify . put_unchecked ( 0 , " invokeruid " , invoker - > getUid ( ) ) ;
2021-02-05 08:20:11 -05:00
}
2020-02-01 08:32:16 -05:00
2021-04-14 21:28:08 -04:00
if ( await_disconnect_ack ) {
{
std : : lock_guard flush_lock { client - > flush_mutex } ;
if ( ! client - > disconnect_acknowledged . has_value ( ) ) {
client - > disconnect_acknowledged = std : : make_optional ( false ) ;
} else {
/*
* The disconnect acknowledged flag might already has been set to true in cases
* where we know that the client is aware of the disconnect .
* An example scenario would be when the client sends the ` clientdisconnect ` command .
*/
}
}
client - > sendCommand0 ( notify . build ( ) , false , std : : make_unique < std : : function < void ( bool ) > > ( [ weak_client ] ( bool success ) {
auto self = dynamic_pointer_cast < VoiceClient > ( weak_client . lock ( ) ) ;
if ( ! self ) {
return ;
}
if ( ! success ) {
/* In theory we have no need to disconnect the client any more since a failed acknowledge would do the trick for us */
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 ) ) ;
}
std : : lock_guard flush_lock { self - > flush_mutex } ;
assert ( self - > disconnect_acknowledged . has_value ( ) ) ;
* self - > disconnect_acknowledged = true ;
} ) ) ;
2021-02-05 08:20:11 -05:00
} else {
2021-04-14 21:28:08 -04:00
client - > sendCommand ( notify , false ) ;
2021-02-05 08:20:11 -05:00
}
2021-04-14 21:28:08 -04:00
}
/* close the connection after 5 seconds regardless if we've received a disconnect acknowledge */
client - > close_connection ( chrono : : system_clock : : now ( ) + chrono : : seconds { 5 } ) ;
} ) ;
2020-02-01 08:32:16 -05:00
2020-01-23 20:57:58 -05:00
return true ;
2019-07-17 13:37:18 -04:00
}
2021-04-14 21:28:08 -04:00
bool VoiceClient : : connection_flushed ( ) {
/* Ensure that we've at least send everything we wanted to send */
if ( ! this - > connection - > wait_empty_write_and_prepare_queue ( std : : chrono : : system_clock : : time_point { } ) ) {
/* we still want to write data */
return false ;
}
2020-01-23 20:57:58 -05:00
2020-02-01 08:32:16 -05:00
{
2021-04-14 21:28:08 -04:00
std : : lock_guard flush_lock { this - > flush_mutex } ;
if ( this - > disconnect_acknowledged . has_value ( ) ) {
/* We've send a disconnect command to the client. If the client acknowledges this command we've nothing more to send. */
return * this - > disconnect_acknowledged ;
2020-02-01 08:32:16 -05:00
}
2020-01-23 20:57:58 -05:00
}
2021-04-14 21:28:08 -04:00
if ( this - > connection - > packet_encoder ( ) . acknowledge_manager ( ) . awaiting_acknowledge ( ) > 0 ) {
/* We're still waiting for some acknowledges from the client (for example the disconnect/kick packet) */
return false ;
}
return true ;
}
2021-03-21 18:51:37 -04:00
2021-04-14 21:28:08 -04:00
bool VoiceClient : : close_connection ( const system_clock : : time_point & timeout ) {
std : : lock_guard flush_lock { this - > flush_mutex } ;
if ( this - > flush_executed ) {
/* We've already scheduled a connection close. We only update the timeout. */
this - > flush_timeout = std : : min ( timeout , this - > flush_timeout ) ;
2020-12-15 17:21:20 -05:00
return true ;
}
2020-12-05 10:27:41 -05:00
2021-04-14 21:28:08 -04:00
this - > flush_timeout = timeout ;
auto weak_client { this - > weak_ref ( ) } ;
serverInstance - > general_task_executor ( ) - > schedule_repeating ( this - > flush_task , " connection flush/close for " + this - > getLoggingPrefix ( ) , std : : chrono : : milliseconds { 25 } , [ weak_client ] ( const auto & ) {
auto client = dynamic_pointer_cast < VoiceClient > ( weak_client . lock ( ) ) ;
if ( ! client ) {
/* client has already been deallocated */
return ;
2020-02-01 08:32:16 -05:00
}
2020-01-23 20:57:58 -05:00
2021-04-14 21:28:08 -04:00
/* Dont execute any commands which might alter the current client state */
std : : lock_guard command_lock { client - > command_lock } ;
2020-01-23 20:57:58 -05:00
2021-04-14 21:28:08 -04:00
{
std : : lock_guard state_lock { client - > state_lock } ;
switch ( client - > state ) {
case ConnectionState : : DISCONNECTED :
/* somebody else already disconnected the client */
return ;
case ConnectionState : : CONNECTED :
case ConnectionState : : INIT_HIGH :
case ConnectionState : : INIT_LOW :
/* It's the first call to this task. Setting the clients state to disconnecting */
break ;
2020-02-01 08:32:16 -05:00
2021-04-14 21:28:08 -04:00
case ConnectionState : : DISCONNECTING :
/* We're just awaiting for the client to finish stuff */
2020-01-23 20:57:58 -05:00
break ;
2021-04-14 21:28:08 -04:00
case ConnectionState : : UNKNWON :
default :
assert ( false ) ;
return ;
2020-01-23 20:57:58 -05:00
}
2021-04-14 21:28:08 -04:00
client - > state = ConnectionState : : DISCONNECTING ;
2020-01-23 20:57:58 -05:00
}
2020-02-01 08:32:16 -05:00
2021-04-14 21:28:08 -04:00
auto timestamp_now = std : : chrono : : system_clock : : now ( ) ;
auto flushed = client - > connection_flushed ( ) ;
if ( ! flushed & & client - > flush_timeout > = timestamp_now ) {
/* connection hasn't yet been flushed */
return ;
}
if ( flushed ) {
debugMessage ( client - > getServerId ( ) , " {} Connection successfully flushed. " , client - > getLoggingPrefix ( ) ) ;
} else {
debugMessage ( client - > getServerId ( ) , " {} Connection flush timed out. Force closing connection. " , client - > getLoggingPrefix ( ) ) ;
}
/* connection flushed or flush timed out. */
client - > finalDisconnect ( ) ;
2020-02-01 08:32:16 -05:00
} ) ;
2020-12-05 10:27:41 -05:00
2020-01-23 20:57:58 -05:00
return true ;
2019-07-17 13:37:18 -04:00
}
void VoiceClient : : finalDisconnect ( ) {
2021-02-21 14:28:59 -05:00
auto ownLock = dynamic_pointer_cast < VoiceClient > ( this - > ref ( ) ) ;
2020-01-23 20:57:58 -05:00
assert ( ownLock ) ;
2021-04-14 21:28:08 -04:00
{
std : : lock_guard state_lock { this - > state_lock } ;
if ( this - > state ! = ConnectionState : : DISCONNECTING ) {
logCritical ( this - > getServerId ( ) , " {} finalDisconnect called but state isn't disconnecting ({}). " , this - > getLoggingPrefix ( ) , ( uint32_t ) this - > state ) ;
return ;
}
this - > state = ConnectionState : : DISCONNECTED ;
2020-01-23 20:57:58 -05:00
}
{
2021-04-14 21:28:08 -04:00
std : : lock_guard flush_lock { this - > flush_mutex } ;
this - > flush_executed = true ;
if ( this - > flush_task ) {
serverInstance - > general_task_executor ( ) - > cancel_task ( this - > flush_task ) ;
this - > flush_task = 0 ;
2020-12-05 10:27:41 -05:00
}
2020-01-23 20:57:58 -05:00
}
2021-04-14 21:28:08 -04:00
/* TODO: Remove? (legacy) */
{
lock_guard disconnect_lock_final ( this - > finalDisconnectLock ) ;
}
std : : lock_guard command_lock { this - > command_lock } ;
this - > processLeave ( ) ;
2020-12-17 06:00:27 -05:00
if ( this - > voice_server ) {
this - > voice_server - > unregisterConnection ( ownLock ) ;
}
2019-07-17 13:37:18 -04:00
}
void VoiceClient : : send_voice_packet ( const pipes : : buffer_view & voice_buffer , const SpeakingClient : : VoicePacketFlags & flags ) {
2021-02-05 11:20:39 -05:00
protocol : : PacketFlags packet_flags { ( uint8_t ) PacketFlag : : None } ;
packet_flags | = flags . encrypted ? PacketFlag : : None : PacketFlag : : Unencrypted ;
packet_flags | = flags . head ? PacketFlag : : Compressed : PacketFlag : : None ;
packet_flags | = flags . fragmented ? PacketFlag : : Fragmented : PacketFlag : : None ;
packet_flags | = flags . new_protocol ? PacketFlag : : NewProtocol : PacketFlag : : None ;
2020-01-23 20:57:58 -05:00
2020-04-24 16:04:07 -04:00
this - > connection - > send_packet ( PacketType : : VOICE , packet_flags , voice_buffer . data_ptr < void > ( ) , voice_buffer . length ( ) ) ;
2019-07-17 13:37:18 -04:00
}
2020-11-28 05:09:25 -05:00
void VoiceClient : : send_voice ( const std : : shared_ptr < SpeakingClient > & source_client , uint16_t seq_no , uint8_t codec , const void * payload , size_t payload_length ) {
/* TODO: Somehow set the head (compressed) flag for beginning voice packets? */
2021-02-07 06:27:40 -05:00
auto packet = protocol : : allocate_outgoing_server_packet ( payload_length + 5 ) ;
packet - > type_and_flags_ = protocol : : PacketType : : VOICE ;
2020-11-28 05:09:25 -05:00
2021-02-07 06:27:40 -05:00
auto packet_payload = ( uint8_t * ) packet - > payload ;
* ( ( uint16_t * ) packet_payload + 0 ) = htons ( seq_no ) ;
* ( ( uint16_t * ) packet_payload + 1 ) = htons ( source_client - > getClientId ( ) ) ;
packet_payload [ 4 ] = codec ;
2020-01-23 20:57:58 -05:00
2020-11-28 05:09:25 -05:00
if ( payload ) {
memcpy ( packet - > payload + 5 , payload , payload_length ) ;
2020-09-06 15:00:27 -04:00
} else {
2020-11-28 05:09:25 -05:00
assert ( payload_length = = 0 ) ;
2020-09-06 15:00:27 -04:00
}
2020-11-28 05:09:25 -05:00
this - > getConnection ( ) - > send_packet ( packet ) ;
}
void VoiceClient : : send_voice_whisper ( const std : : shared_ptr < SpeakingClient > & source_client , uint16_t seq_no , uint8_t codec , const void * payload , size_t payload_length ) {
bool head { false } ;
if ( this - > whisper_head_counter < 5 ) {
head = true ;
this - > whisper_head_counter + + ;
}
if ( ! payload ) {
this - > whisper_head_counter = 0 ;
}
protocol : : OutgoingServerPacket * packet ;
size_t payload_offset { 0 } ;
if ( head & & this - > getType ( ) = = ClientType : : CLIENT_TEASPEAK ) {
auto uniqueId = source_client - > getUid ( ) ;
auto nickname = source_client - > getDisplayName ( ) ;
if ( uniqueId . length ( ) > kWhisperClientUniqueIdLength ) {
logCritical ( LOG_GENERAL , " Clients unique id is longer than the expected max length of {}. Unique length: {} " , kWhisperClientUniqueIdLength , uniqueId . length ( ) ) ;
return ;
}
if ( nickname . length ( ) > kMaxWhisperClientNameLength ) {
logCritical ( LOG_GENERAL , " Clients name is longer than the expected max length of {}. Name length: {} " , kMaxWhisperClientNameLength , nickname . length ( ) ) ;
return ;
}
2021-02-07 06:27:40 -05:00
packet = protocol : : allocate_outgoing_server_packet (
payload_length + 5 + kWhisperClientUniqueIdLength + 1 + nickname . length ( ) ) ;
packet - > type_and_flags_ | = protocol : : PacketFlag : : Compressed ;
2020-11-28 05:09:25 -05:00
memset ( packet - > payload + payload_offset , 0 , kWhisperClientUniqueIdLength ) ;
memcpy ( packet - > payload + payload_offset , uniqueId . data ( ) , uniqueId . length ( ) ) ;
payload_offset + = kWhisperClientUniqueIdLength ;
packet - > payload [ payload_offset + + ] = nickname . length ( ) ;
memcpy ( packet - > payload + payload_offset , nickname . data ( ) , nickname . length ( ) ) ;
payload_offset + = nickname . length ( ) ;
} else {
2021-02-07 06:27:40 -05:00
packet = protocol : : allocate_outgoing_server_packet ( payload_length + 5 ) ;
2020-11-28 05:09:25 -05:00
}
2021-02-07 06:27:40 -05:00
packet - > type_and_flags_ | = protocol : : PacketType : : VOICE_WHISPER ;
2020-11-28 05:09:25 -05:00
* ( ( uint16_t * ) & packet - > payload [ payload_offset ] ) = htons ( seq_no ) ;
payload_offset + = 2 ;
* ( ( uint16_t * ) & packet - > payload [ payload_offset ] ) = htons ( source_client - > getClientId ( ) ) ;
payload_offset + = 2 ;
packet - > payload [ payload_offset ] = codec ;
payload_offset + = 1 ;
if ( payload ) {
memcpy ( packet - > payload + payload_offset , payload , payload_length ) ;
payload_offset + = payload_length ;
} else {
assert ( payload_length = = 0 ) ;
}
packet - > payload_size = payload_offset ;
this - > getConnection ( ) - > send_packet ( packet ) ;
2020-04-08 07:01:41 -04:00
}
float VoiceClient : : current_ping_deviation ( ) {
2020-07-29 13:05:38 -04:00
return this - > connection - > packet_encoder ( ) . acknowledge_manager ( ) . current_rttvar ( ) ;
2020-04-08 07:01:41 -04:00
}
float VoiceClient : : current_packet_loss ( ) const {
return this - > connection - > packet_statistics ( ) . current_packet_loss ( ) ;
2020-11-07 07:17:51 -05:00
}
void VoiceClient : : processJoin ( ) {
SpeakingClient : : processJoin ( ) ;
if ( this - > rtc_client_id > 0 ) {
2020-11-28 05:09:25 -05:00
{
/* Normal audio channel */
auto sender = this - > server - > rtc_server ( ) . create_audio_source_supplier_sender ( this - > rtc_client_id , 1 ) ;
assert ( sender . has_value ( ) ) ;
this - > rtc_audio_supplier . reset ( * sender ) ;
}
{
/* Audio whisper channel */
auto sender = this - > server - > rtc_server ( ) . create_audio_source_supplier_sender ( this - > rtc_client_id , 2 ) ;
assert ( sender . has_value ( ) ) ;
this - > rtc_audio_whisper_supplier . reset ( * sender ) ;
}
2020-11-07 07:17:51 -05:00
}
}
2021-04-21 07:19:46 -04:00
void VoiceClient : : clear_video_unsupported_message_flag ( ) {
this - > video_unsupported_message_send = false ;
}
constexpr static auto kUnsupportedMessage { " \n "
" [b]Somebody in your channel started video sharing! \n "
" [color=red]Your client doesn't support video sharing.[/color] \n "
" \n "
" Download the newest TeaSpeak client from [url]https://teaspeak.de[/url] or \n "
" use the TeaSpeakWeb client at [url]%web-url%[/url].[/b] " } ;
void VoiceClient : : send_video_unsupported_message ( ) {
if ( this - > video_unsupported_message_send ) {
return ;
}
this - > video_unsupported_message_send = true ;
ts : : command_builder notify { " notifytextmessage " } ;
notify . put_unchecked ( 0 , " targetmode " , ChatMessageMode : : TEXTMODE_CHANNEL ) ;
notify . put_unchecked ( 0 , " target " , " 0 " ) ;
notify . put_unchecked ( 0 , " invokerid " , " 0 " ) ;
notify . put_unchecked ( 0 , " invokername " , " TeaSpeak - Video " ) ;
notify . put_unchecked ( 0 , " invokeruid " , " 000000000000000000000000000 " ) ;
std : : string message { kUnsupportedMessage } ;
if ( auto index { message . find ( " %web-url% " ) } ; index ! = std : : string : : npos ) {
/* TODO: generate connect url */
//this->server->getWebServer()->
message . replace ( index , 9 , " https://web.teaspeak.de " ) ;
}
notify . put_unchecked ( 0 , " msg " , message ) ;
this - > sendCommand ( notify , false ) ;
}