2020-03-30 11:44:18 +00:00
import {
AbstractServerConnection ,
CommandOptionDefaults ,
CommandOptions ,
2020-04-09 13:10:14 +00:00
ConnectionStateListener ,
2020-03-30 11:44:18 +00:00
} from "tc-shared/connection/ConnectionBase" ;
import { ConnectionHandler , ConnectionState , DisconnectReason } from "tc-shared/ConnectionHandler" ;
import { HandshakeHandler } from "tc-shared/connection/HandshakeHandler" ;
import { ConnectionCommandHandler , ServerConnectionCommandBoss } from "tc-shared/connection/CommandHandler" ;
import { CommandResult } from "tc-shared/connection/ServerConnectionDeclaration" ;
import { settings , Settings } from "tc-shared/settings" ;
import * as log from "tc-shared/log" ;
2020-09-03 10:14:15 +00:00
import { LogCategory , logTrace } from "tc-shared/log" ;
2020-03-30 11:44:18 +00:00
import { Regex } from "tc-shared/ui/modal/ModalConnect" ;
import { AbstractCommandHandlerBoss } from "tc-shared/connection/AbstractCommandHandler" ;
import { VoiceConnection } from "../voice/VoiceHandler" ;
2020-07-21 22:55:28 +00:00
import { EventType } from "tc-shared/ui/frames/log/Definitions" ;
2020-07-23 22:29:36 +00:00
import { WrappedWebSocket } from "tc-backend/web/connection/WrappedWebSocket" ;
2020-08-10 12:41:34 +00:00
import { AbstractVoiceConnection } from "tc-shared/connection/VoiceConnection" ;
import { DummyVoiceConnection } from "tc-shared/connection/DummyVoiceConnection" ;
2020-09-03 10:14:15 +00:00
import { parseCommand } from "tc-backend/web/connection/CommandParser" ;
2020-09-16 19:50:21 +00:00
import { ServerAddress } from "tc-shared/tree/Server" ;
2020-03-30 11:44:18 +00:00
2019-02-23 13:15:22 +00:00
class ReturnListener < T > {
resolve : ( value? : T | PromiseLike < T > ) = > void ;
reject : ( reason? : any ) = > void ;
code : string ;
2020-07-19 15:12:41 +00:00
timeout : number ;
2019-02-23 13:15:22 +00:00
}
2020-07-23 22:29:36 +00:00
let globalReturnCodeIndex = 0 ;
2020-03-30 11:44:18 +00:00
export class ServerConnection extends AbstractServerConnection {
2020-07-23 22:29:36 +00:00
private remoteServerAddress : ServerAddress ;
private handshakeHandler : HandshakeHandler ;
2019-11-23 22:41:37 +00:00
2020-07-23 22:29:36 +00:00
private commandHandlerBoss : ServerConnectionCommandBoss ;
private defaultCommandHandler : ConnectionCommandHandler ;
2019-11-23 22:41:37 +00:00
2020-07-23 22:29:36 +00:00
private socket : WrappedWebSocket ;
private connectCancelCallback : ( ) = > void ;
2019-11-23 22:41:37 +00:00
2020-07-23 22:29:36 +00:00
private returnListeners : ReturnListener < CommandResult > [ ] = [ ] ;
2019-02-23 13:15:22 +00:00
2020-03-30 11:44:18 +00:00
private _connection_state_listener : ConnectionStateListener ;
2020-08-10 12:41:34 +00:00
private dummyVoiceConnection : DummyVoiceConnection ;
private voiceConnection : VoiceConnection ;
2019-04-04 19:47:52 +00:00
2020-07-23 22:29:36 +00:00
private pingStatistics = {
2020-03-30 11:44:18 +00:00
thread_id : 0 ,
2019-08-21 08:00:01 +00:00
2020-07-23 22:29:36 +00:00
lastRequestTimestamp : 0 ,
lastResponseTimestamp : 0 ,
currentRequestId : 0 ,
2019-08-21 08:00:01 +00:00
2020-03-30 11:44:18 +00:00
interval : 5000 ,
timeout : 7500 ,
2019-08-21 08:00:01 +00:00
2020-07-23 22:29:36 +00:00
currentJsValue : 0 ,
currentNativeValue : 0 /* ping value for native (WS) */
2020-03-30 11:44:18 +00:00
} ;
2019-08-21 08:00:01 +00:00
2020-03-30 11:44:18 +00:00
constructor ( client : ConnectionHandler ) {
super ( client ) ;
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
this . commandHandlerBoss = new ServerConnectionCommandBoss ( this ) ;
this . defaultCommandHandler = new ConnectionCommandHandler ( this ) ;
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
this . commandHandlerBoss . register_handler ( this . defaultCommandHandler ) ;
2020-03-30 11:44:18 +00:00
this . command_helper . initialize ( ) ;
2019-04-04 19:47:52 +00:00
2020-08-10 12:41:34 +00:00
if ( ! settings . static_global ( Settings . KEY_DISABLE_VOICE , false ) ) {
this . voiceConnection = new VoiceConnection ( this ) ;
} else {
this . dummyVoiceConnection = new DummyVoiceConnection ( this ) ;
}
2020-03-30 11:44:18 +00:00
}
2019-02-23 13:15:22 +00:00
2020-03-30 11:44:18 +00:00
destroy() {
this . disconnect ( "handle destroyed" ) . catch ( error = > {
log . warn ( LogCategory . NETWORKING , tr ( "Failed to disconnect on server connection destroy: %o" ) , error ) ;
} ) . then ( ( ) = > {
2020-07-23 22:29:36 +00:00
clearInterval ( this . pingStatistics . thread_id ) ;
if ( this . connectCancelCallback )
this . connectCancelCallback ( ) ;
2019-08-21 08:00:01 +00:00
2020-07-23 22:29:36 +00:00
for ( const listener of this . returnListeners ) {
2020-03-30 11:44:18 +00:00
try {
listener . reject ( "handler destroyed" ) ;
} catch ( error ) {
log . warn ( LogCategory . NETWORKING , tr ( "Failed to reject command promise: %o" ) , error ) ;
2019-08-21 08:00:01 +00:00
}
2020-03-30 11:44:18 +00:00
}
2020-07-23 22:29:36 +00:00
this . returnListeners = undefined ;
2019-08-21 08:00:01 +00:00
2020-03-30 11:44:18 +00:00
this . command_helper . destroy ( ) ;
2019-08-21 08:00:01 +00:00
2020-07-23 22:29:36 +00:00
this . defaultCommandHandler && this . commandHandlerBoss . unregister_handler ( this . defaultCommandHandler ) ;
this . defaultCommandHandler = undefined ;
2019-08-21 08:00:01 +00:00
2020-08-10 12:41:34 +00:00
this . voiceConnection && this . voiceConnection . destroy ( ) ;
this . voiceConnection = undefined ;
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
this . commandHandlerBoss && this . commandHandlerBoss . destroy ( ) ;
this . commandHandlerBoss = undefined ;
2020-08-10 12:41:34 +00:00
this . events . destroy ( ) ;
2020-03-30 11:44:18 +00:00
} ) ;
}
2019-02-23 13:15:22 +00:00
2020-03-30 11:44:18 +00:00
async connect ( address : ServerAddress , handshake : HandshakeHandler , timeout? : number ) : Promise < void > {
2020-07-23 22:29:36 +00:00
const connectBeginTimestamp = Date . now ( ) ;
2020-03-30 11:44:18 +00:00
timeout = typeof ( timeout ) === "number" ? timeout : 5000 ;
2019-11-23 22:41:37 +00:00
2020-03-30 11:44:18 +00:00
try {
2020-07-23 22:29:36 +00:00
await this . disconnect ( ) ;
2020-03-30 11:44:18 +00:00
} catch ( error ) {
log . error ( LogCategory . NETWORKING , tr ( "Failed to close old connection properly. Error: %o" ) , error ) ;
throw "failed to cleanup old connection" ;
}
2019-02-23 13:15:22 +00:00
2020-03-30 11:44:18 +00:00
this . updateConnectionState ( ConnectionState . CONNECTING ) ;
2020-07-23 22:29:36 +00:00
this . remoteServerAddress = address ;
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
this . handshakeHandler = handshake ;
this . handshakeHandler . setConnection ( this ) ;
2019-11-23 22:41:37 +00:00
2020-03-30 11:44:18 +00:00
/* The direct one connect directly to the target address. The other via the .con-gate.work */
2020-07-23 22:29:36 +00:00
let availableSockets : WrappedWebSocket [ ] = [ ] ;
proxySocket :
if ( ! settings . static_global ( Settings . KEY_CONNECT_NO_DNSPROXY ) ) {
let host ;
if ( Regex . IP_V4 . test ( address . host ) ) {
host = address . host . replace ( /\./g , "-" ) + ".con-gate.work" ;
} else if ( Regex . IP_V6 . test ( address . host ) ) {
host = address . host . replace ( /\[(.*)]/ , "$1" ) . replace ( /:/g , "_" ) + ".con-gate.work" ;
} else {
break proxySocket ;
2020-03-30 11:44:18 +00:00
}
2019-11-22 20:51:26 +00:00
2020-07-25 11:56:30 +00:00
availableSockets . push ( new WrappedWebSocket ( {
host : host ,
port : address.port ,
secure : true
} ) )
2020-07-23 22:29:36 +00:00
}
2020-07-25 11:56:30 +00:00
availableSockets . push ( new WrappedWebSocket ( {
host : address.host ,
port : address.port ,
secure : true
} ) ) ;
2020-07-23 22:29:36 +00:00
let timeoutRaised = false ;
let timeoutPromise = new Promise ( resolve = > setTimeout ( ( ) = > {
timeoutRaised = true ;
resolve ( ) ;
} , timeout ) ) ;
let cancelRaised = false ;
let cancelPromise = new Promise ( resolve = > {
this . connectCancelCallback = ( ) = > {
this . connectCancelCallback = undefined ;
cancelRaised = true ;
resolve ( ) ;
} ;
} ) ;
2019-08-21 08:00:01 +00:00
2020-07-23 22:29:36 +00:00
availableSockets . forEach ( e = > e . doConnect ( ) ) ;
while ( availableSockets . length > 0 ) {
await Promise . race ( [ . . . availableSockets . map ( e = > e . awaitConnectResult ( ) ) , timeoutPromise , cancelPromise ] ) ;
2019-08-21 08:00:01 +00:00
2020-07-23 22:29:36 +00:00
if ( cancelRaised ) {
log . debug ( LogCategory . NETWORKING , tr ( "Aborting connect attempt due to a cancel request." ) ) ;
availableSockets . forEach ( e = > e . closeConnection ( ) ) ;
return
}
2019-11-23 22:41:37 +00:00
2020-07-23 22:29:36 +00:00
if ( timeoutRaised ) {
log . info ( LogCategory . NETWORKING , tr ( "Connect timeout triggered. Aborting connect attempt!" ) ) ;
availableSockets . forEach ( e = > e . closeConnection ( ) ) ;
this . updateConnectionState ( ConnectionState . UNCONNECTED ) ; /* firstly update the state, that fire event */
this . client . handleDisconnect ( DisconnectReason . CONNECT_FAILURE ) ;
return
}
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
let finished = availableSockets . find ( e = > e . state !== "connecting" ) ;
if ( ! finished ) continue ; /* should not happen, but we want to ensure it */
availableSockets . remove ( finished ) ;
2020-03-30 11:44:18 +00:00
2020-07-23 22:29:36 +00:00
switch ( finished . state ) {
case "unconnected" :
2020-07-25 11:56:30 +00:00
log . debug ( LogCategory . NETWORKING , tr ( "Connection attempt to %s:%d via %s got aborted." ) , this . remoteServerAddress . host , this . remoteServerAddress . port , finished . socketUrl ( ) ) ;
2020-07-23 22:29:36 +00:00
continue ;
2020-03-30 11:44:18 +00:00
2020-07-23 22:29:36 +00:00
case "errored" :
const error = finished . popError ( ) ;
2020-07-25 11:56:30 +00:00
log . info ( LogCategory . NETWORKING , tr ( "Connection attempt to %s:%d via %s failed:\n%o" ) , this . remoteServerAddress . host , this . remoteServerAddress . port , finished . socketUrl ( ) , error ) ;
2020-07-23 22:29:36 +00:00
continue ;
2020-03-30 11:44:18 +00:00
2020-07-23 22:29:36 +00:00
case "connected" :
break ;
2020-03-30 11:44:18 +00:00
}
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
this . socket = finished ;
2019-11-23 22:41:37 +00:00
2020-07-23 22:29:36 +00:00
/* abort any other ongoing connection attempts, we already succeeded */
availableSockets . forEach ( e = > e . closeConnection ( ) ) ;
break ;
}
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
if ( ! this . socket ) {
log . info ( LogCategory . NETWORKING , tr ( "Failed to connect to %s:%d. No connection attempt succeeded." ) , this . remoteServerAddress . host , this . remoteServerAddress . port ) ;
this . updateConnectionState ( ConnectionState . UNCONNECTED ) ; /* firstly update the state, that fire event */
this . client . handleDisconnect ( DisconnectReason . CONNECT_FAILURE ) ;
return ;
}
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
this . socket . callbackMessage = message = > this . handleSocketMessage ( message ) ;
this . socket . callbackDisconnect = ( code , reason ) = > {
try {
this . disconnect ( ) ;
} catch ( error ) {
log . warn ( LogCategory . NETWORKING , tr ( "Failed to disconnect with an already closed socket: %o" ) , error ) ;
}
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
this . client . handleDisconnect ( DisconnectReason . CONNECTION_CLOSED , {
code : code ,
reason : reason
} ) ;
} ;
this . socket . callbackErrored = ( ) = > {
if ( this . socket . hasError ( ) ) {
log . error ( LogCategory . NETWORKING , tr ( "Server connection %s:%d has been terminated due to an unexpected error (%o)." ) ,
this . remoteServerAddress . host ,
this . remoteServerAddress . port ,
this . socket . popError ( )
) ;
} else {
log . error ( LogCategory . NETWORKING , tr ( "Server connection %s:%d has been terminated due to an unexpected error." ) , this . remoteServerAddress . host , this . remoteServerAddress . port ) ;
}
try {
this . disconnect ( ) ;
} catch ( error ) {
log . warn ( LogCategory . NETWORKING , tr ( "Failed to disconnect with an already closed socket: %o" ) , error ) ;
}
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
this . client . handleDisconnect ( DisconnectReason . CONNECTION_CLOSED ) ;
} ;
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
const connectEndTimestamp = Date . now ( ) ;
log . info ( LogCategory . NETWORKING , tr ( "Successfully initialized a connection to %s:%d via %s within %d milliseconds." ) ,
this . remoteServerAddress . host ,
this . remoteServerAddress . port ,
2020-07-25 11:56:30 +00:00
this . socket . socketUrl ( ) ,
2020-07-23 22:29:36 +00:00
connectEndTimestamp - connectBeginTimestamp ) ;
2019-08-21 08:00:01 +00:00
2020-09-03 10:14:15 +00:00
/* enabling raw commands, if the server supports it */
this . sendData ( JSON . stringify ( {
type : "enable-raw-commands"
} ) )
2020-07-23 22:29:36 +00:00
this . start_handshake ( ) ;
2020-03-30 11:44:18 +00:00
}
2019-02-23 13:15:22 +00:00
2020-03-30 11:44:18 +00:00
private start_handshake() {
this . updateConnectionState ( ConnectionState . INITIALISING ) ;
2020-07-21 22:55:28 +00:00
this . client . log . log ( EventType . CONNECTION_LOGIN , { } ) ;
2020-07-23 22:29:36 +00:00
this . handshakeHandler . initialize ( ) ;
this . handshakeHandler . startHandshake ( ) ;
2020-03-30 11:44:18 +00:00
}
2019-11-23 22:41:37 +00:00
2020-03-30 11:44:18 +00:00
async disconnect ( reason? : string ) : Promise < void > {
2020-07-23 22:29:36 +00:00
if ( this . connectCancelCallback )
this . connectCancelCallback ( ) ;
2020-08-10 12:41:34 +00:00
if ( this . connectionState === ConnectionState . UNCONNECTED )
2020-07-23 22:41:04 +00:00
return ;
2020-04-09 13:10:14 +00:00
this . updateConnectionState ( ConnectionState . DISCONNECTING ) ;
try {
2020-07-23 22:29:36 +00:00
clearTimeout ( this . pingStatistics . thread_id ) ;
this . pingStatistics . thread_id = undefined ;
2019-02-23 13:15:22 +00:00
2020-04-09 13:10:14 +00:00
if ( typeof ( reason ) === "string" ) {
//TODO send disconnect reason
}
2019-08-21 08:00:01 +00:00
2020-07-23 22:29:36 +00:00
if ( this . socket ) {
this . socket . callbackMessage = undefined ;
this . socket . callbackDisconnect = undefined ;
this . socket . callbackErrored = undefined ;
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
this . socket . closeConnection ( ) ; /* 3000 + 0xFF, tr("request disconnect") */
this . socket = undefined ;
}
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
for ( let future of this . returnListeners )
2020-04-09 13:10:14 +00:00
future . reject ( tr ( "Connection closed" ) ) ;
2020-07-23 22:29:36 +00:00
this . returnListeners = [ ] ;
2020-04-09 13:10:14 +00:00
} finally {
this . updateConnectionState ( ConnectionState . UNCONNECTED ) ;
}
2020-03-30 11:44:18 +00:00
}
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
private handleSocketMessage ( data ) {
2020-03-30 11:44:18 +00:00
if ( typeof ( data ) === "string" ) {
let json ;
try {
json = JSON . parse ( data ) ;
} catch ( e ) {
log . warn ( LogCategory . NETWORKING , tr ( "Could not parse message json!" ) ) ;
return ;
}
if ( json [ "type" ] === undefined ) {
log . warn ( LogCategory . NETWORKING , tr ( "Missing data type in message!" ) ) ;
return ;
}
if ( json [ "type" ] === "command" ) {
2020-04-18 18:25:58 +00:00
/* devel-block(log-networking-commands) */
2020-03-30 11:44:18 +00:00
let group = log . group ( log . LogType . DEBUG , LogCategory . NETWORKING , tr ( "Handling command '%s'" ) , json [ "command" ] ) ;
group . log ( tr ( "Handling command '%s'" ) , json [ "command" ] ) ;
group . group ( log . LogType . TRACE , tr ( "Json:" ) ) . collapsed ( true ) . log ( "%o" , json ) . end ( ) ;
2020-04-18 18:25:58 +00:00
/* devel-block-end */
2020-03-30 11:44:18 +00:00
2020-07-23 22:29:36 +00:00
this . commandHandlerBoss . invoke_handle ( {
2020-03-30 11:44:18 +00:00
command : json [ "command" ] ,
arguments : json [ "data" ]
} ) ;
if ( json [ "command" ] === "initserver" ) {
2020-07-23 22:29:36 +00:00
this . pingStatistics . thread_id = setInterval ( ( ) = > this . doNextPing ( ) , this . pingStatistics . interval ) as any ;
this . doNextPing ( ) ;
2020-03-30 11:44:18 +00:00
this . updateConnectionState ( ConnectionState . CONNECTED ) ;
}
2020-04-18 18:25:58 +00:00
/* devel-block(log-networking-commands) */
2020-03-30 11:44:18 +00:00
group . end ( ) ;
2020-04-18 18:25:58 +00:00
/* devel-block-end */
2020-09-03 10:14:15 +00:00
} else if ( json [ "type" ] === "command-raw" ) {
const command = parseCommand ( json [ "payload" ] ) ;
logTrace ( LogCategory . NETWORKING , tr ( "Received command %s" ) , command . command ) ;
this . commandHandlerBoss . invoke_handle ( {
command : command.command ,
arguments : command.payload
} ) ;
if ( command . command === "initserver" ) {
this . pingStatistics . thread_id = setInterval ( ( ) = > this . doNextPing ( ) , this . pingStatistics . interval ) as any ;
this . doNextPing ( ) ;
this . updateConnectionState ( ConnectionState . CONNECTED ) ;
}
2020-03-30 11:44:18 +00:00
} else if ( json [ "type" ] === "WebRTC" ) {
2020-08-10 17:39:28 +00:00
this . voiceConnection ? . handleControlPacket ( json ) ;
2020-03-30 11:44:18 +00:00
} else if ( json [ "type" ] === "ping" ) {
2020-07-23 22:29:36 +00:00
this . sendData ( JSON . stringify ( {
type : 'pong' ,
payload : json [ "payload" ]
} ) ) ;
2020-03-30 11:44:18 +00:00
} else if ( json [ "type" ] === "pong" ) {
const id = parseInt ( json [ "payload" ] ) ;
2020-07-23 22:29:36 +00:00
if ( id != this . pingStatistics . currentRequestId ) {
log . warn ( LogCategory . NETWORKING , tr ( "Received pong which is older than the last request. Delay may over %oms? (Index: %o, Current index: %o)" ) , this . pingStatistics . timeout , id , this . pingStatistics . currentRequestId ) ;
2019-08-21 08:00:01 +00:00
} else {
2020-07-23 22:29:36 +00:00
this . pingStatistics . lastResponseTimestamp = 'now' in performance ? performance . now ( ) : Date . now ( ) ;
this . pingStatistics . currentJsValue = this . pingStatistics . lastResponseTimestamp - this . pingStatistics . lastRequestTimestamp ;
this . pingStatistics . currentNativeValue = parseInt ( json [ "ping_native" ] ) / 1000 ; /* we're getting it in microseconds and not milliseconds */
2020-06-10 16:13:56 +00:00
//log.debug(LogCategory.NETWORKING, tr("Received new pong. Updating ping to: JS: %o Native: %o"), this._ping.value.toFixed(3), this._ping.value_native.toFixed(3));
2019-02-23 13:15:22 +00:00
}
} else {
2020-03-30 11:44:18 +00:00
log . warn ( LogCategory . NETWORKING , tr ( "Unknown command type %o" ) , json [ "type" ] ) ;
2019-02-23 13:15:22 +00:00
}
2020-03-30 11:44:18 +00:00
} else {
log . warn ( LogCategory . NETWORKING , tr ( "Received unknown message of type %s. Dropping message" ) , typeof ( data ) ) ;
2019-02-23 13:15:22 +00:00
}
2020-03-30 11:44:18 +00:00
}
2019-02-23 13:15:22 +00:00
2020-03-30 11:44:18 +00:00
sendData ( data : any ) {
2020-07-23 22:29:36 +00:00
if ( ! this . socket || this . socket . state !== "connected" ) {
log . warn ( LogCategory . NETWORKING , tr ( "Tried to send data via a non connected server socket." ) ) ;
2020-03-30 11:44:18 +00:00
return ;
2019-02-23 13:15:22 +00:00
}
2020-07-23 22:29:36 +00:00
this . socket . socket . send ( data ) ;
2020-03-30 11:44:18 +00:00
}
2019-02-23 13:15:22 +00:00
2020-07-23 22:29:36 +00:00
private static commandDataToJson ( input : any ) : string {
2020-03-30 11:44:18 +00:00
return JSON . stringify ( input , ( key , value ) = > {
switch ( typeof value ) {
case "boolean" : return value == true ? "1" : "0" ;
case "function" : return value ( ) ;
default :
return value ;
2019-02-23 13:15:22 +00:00
}
2020-03-30 11:44:18 +00:00
} ) ;
2019-02-23 13:15:22 +00:00
2020-03-30 11:44:18 +00:00
}
2019-04-18 11:19:08 +00:00
2020-03-30 11:44:18 +00:00
send_command ( command : string , data? : any | any [ ] , _options? : CommandOptions ) : Promise < CommandResult > {
2020-07-23 22:29:36 +00:00
if ( ! this . socket || ! this . connected ( ) ) {
2020-03-30 11:44:18 +00:00
log . warn ( LogCategory . NETWORKING , tr ( "Tried to send a command without a valid connection." ) ) ;
return Promise . reject ( tr ( "not connected" ) ) ;
2019-02-23 13:15:22 +00:00
}
2020-03-30 11:44:18 +00:00
const options : CommandOptions = { } ;
Object . assign ( options , CommandOptionDefaults ) ;
Object . assign ( options , _options ) ;
2020-08-10 12:41:34 +00:00
data = Array . isArray ( data ) ? data : [ data || { } ] ;
2020-03-30 11:44:18 +00:00
if ( data . length == 0 ) /* we require min one arg to append return_code */
data . push ( { } ) ;
let result = new Promise < CommandResult > ( ( resolve , failed ) = > {
2020-08-10 12:41:34 +00:00
let payload = Array . isArray ( data ) ? data : [ data ] ;
2020-07-23 22:29:36 +00:00
let returnCode = typeof payload [ 0 ] [ "return_code" ] === "string" ? payload [ 0 ] . return_code : ++ globalReturnCodeIndex ;
payload [ 0 ] . return_code = returnCode ;
2020-03-30 11:44:18 +00:00
let listener = new ReturnListener < CommandResult > ( ) ;
listener . resolve = resolve ;
listener . reject = failed ;
2020-07-23 22:29:36 +00:00
listener . code = returnCode ;
2020-03-30 11:44:18 +00:00
listener . timeout = setTimeout ( ( ) = > {
2020-07-23 22:29:36 +00:00
this . returnListeners . remove ( listener ) ;
2020-03-30 11:44:18 +00:00
listener . reject ( "timeout" ) ;
} , 1500 ) ;
2020-07-23 22:29:36 +00:00
this . returnListeners . push ( listener ) ;
2020-03-30 11:44:18 +00:00
2020-07-23 22:29:36 +00:00
this . sendData ( ServerConnection . commandDataToJson ( {
2020-03-30 11:44:18 +00:00
"type" : "command" ,
"command" : command ,
2020-07-23 22:29:36 +00:00
"data" : payload ,
2020-03-30 11:44:18 +00:00
"flags" : options . flagset . filter ( entry = > entry . length != 0 )
2020-07-23 22:29:36 +00:00
} ) )
2020-03-30 11:44:18 +00:00
} ) ;
2020-07-23 22:29:36 +00:00
return this . defaultCommandHandler . proxy_command_promise ( result , options ) ;
2020-03-30 11:44:18 +00:00
}
2019-02-23 13:15:22 +00:00
2020-03-30 11:44:18 +00:00
connected ( ) : boolean {
2020-07-23 22:29:36 +00:00
return ! ! this . socket && this . socket . state === "connected" ;
2020-03-30 11:44:18 +00:00
}
2019-02-23 13:15:22 +00:00
2020-08-10 12:41:34 +00:00
getVoiceConnection ( ) : AbstractVoiceConnection {
return this . voiceConnection || this . dummyVoiceConnection ;
2020-03-30 11:44:18 +00:00
}
2019-04-04 19:47:52 +00:00
2020-03-30 11:44:18 +00:00
command_handler_boss ( ) : AbstractCommandHandlerBoss {
2020-07-23 22:29:36 +00:00
return this . commandHandlerBoss ;
2020-03-30 11:44:18 +00:00
}
2019-04-04 19:47:52 +00:00
2020-03-30 11:44:18 +00:00
get onconnectionstatechanged ( ) : ConnectionStateListener {
return this . _connection_state_listener ;
}
set onconnectionstatechanged ( listener : ConnectionStateListener ) {
this . _connection_state_listener = listener ;
}
2019-04-15 13:33:51 +00:00
2020-03-30 11:44:18 +00:00
handshake_handler ( ) : HandshakeHandler {
2020-07-23 22:29:36 +00:00
return this . handshakeHandler ;
2020-03-30 11:44:18 +00:00
}
2019-08-21 08:00:01 +00:00
2020-03-30 11:44:18 +00:00
remote_address ( ) : ServerAddress {
2020-07-23 22:29:36 +00:00
return this . remoteServerAddress ;
2020-03-30 11:44:18 +00:00
}
2019-08-21 08:00:01 +00:00
2020-07-25 11:56:30 +00:00
connectionProxyAddress ( ) : ServerAddress | undefined {
return this . socket ? . address ;
}
2020-07-23 22:29:36 +00:00
private doNextPing() {
if ( this . pingStatistics . lastRequestTimestamp + this . pingStatistics . timeout < Date . now ( ) ) {
this . pingStatistics . currentJsValue = this . pingStatistics . timeout ;
this . pingStatistics . lastResponseTimestamp = this . pingStatistics . lastRequestTimestamp + 1 ;
2020-03-30 11:44:18 +00:00
}
2020-07-23 22:29:36 +00:00
if ( this . pingStatistics . lastResponseTimestamp > this . pingStatistics . lastRequestTimestamp ) {
this . pingStatistics . lastRequestTimestamp = 'now' in performance ? performance . now ( ) : Date . now ( ) ;
2020-03-30 11:44:18 +00:00
this . sendData ( JSON . stringify ( {
type : 'ping' ,
2020-07-23 22:29:36 +00:00
payload : ( ++ this . pingStatistics . currentRequestId ) . toString ( )
2020-03-30 11:44:18 +00:00
} ) ) ;
2019-08-21 08:00:01 +00:00
}
2019-04-15 13:33:51 +00:00
}
2020-03-30 11:44:18 +00:00
ping ( ) : { native : number ; javascript? : number } {
return {
2020-07-23 22:29:36 +00:00
javascript : this.pingStatistics.currentJsValue ,
native : this.pingStatistics.currentNativeValue
2020-03-30 11:44:18 +00:00
} ;
2019-02-23 13:15:22 +00:00
}
}