import { accessibilityController , avDevicesController , connectionController , howlerController , roomController , signalsController , twoDimensionalRenderer , uiController , userDataController , userInputController , videoController , watchPartyController } from ".." ; import { SoundParams , HowlerController , chairSounds } from "../sounds/LocalSoundsController" ; import { SignalParams } from "../ui/SignalsController" ; import { VideoStreamingStates } from "../../../shared/shared" ; import { Utilities } from "../utilities/Utilities" ; declare var HIFI_SPACE_NAME ; declare var APP_MODE ; const io = require ( "socket.io-client" ) ; interface WebSocketParticipantData { visitIDHash : string ; currentSeatID : string ; displayName : string ; colorHex : string ; profileImageURL : string ; isAudioInputMuted : boolean ; echoCancellationAvailable : boolean ; echoCancellationEnabled : boolean ; agcAvailable : boolean ; agcEnabled : boolean ; noiseSuppressionAvailable : boolean ; noiseSuppressionEnabled : boolean ; hiFiGainSliderValue : string ; volumeThreshold : number ; currentWatchPartyRoomName : string ; isStreamingVideo : VideoStreamingStates ; } export class WebSocketConnectionController { socket : SocketIOClient . Socket ; readyToSendWebSocketData : boolean = false ; retrievedInitialWebSocketServerData : boolean = false ; constructor ( ) { const socketURL = APP_MODE === "web" ? "" : "https://standup.highfidelity.com" ; this . socket = io ( socketURL , { path : '/socket.io' } ) ; this . socket . on ( "connect" , ( socket ) => { console . log ( `Connected to Socket.IO WebSocket server!` ) ; this . maybeSendInitialWebSocketData ( ) ; } ) ; this . socket . on ( "disconnect" , ( socket ) => { console . log ( `Disconnected from Socket.IO WebSocket server!` ) ; } ) ; this . socket . on ( "onParticipantsAddedOrEdited" , ( participantArray ) => { console . log ( `Retrieved information about ${ participantArray . length } participant(s) from the server. Processing:\n${ JSON . stringify ( participantArray ) }` ) ; participantArray . forEach ( ( participant ) => { let { visitIDHash , currentSeatID , displayName , colorHex , profileImageURL , isAudioInputMuted , echoCancellationAvailable , echoCancellationEnabled , agcAvailable , agcEnabled , noiseSuppressionAvailable , noiseSuppressionEnabled , hiFiGainSliderValue , volumeThreshold , currentWatchPartyRoomName , isStreamingVideo , } = participant ; let localUserData = userDataController . allOtherUserData . find ( ( userData ) => { return userData . visitIDHash === visitIDHash ; } ) ; if ( localUserData ) { let playChairSound = false ; if ( typeof ( displayName ) === "string" ) { localUserData . displayName = displayName ; } if ( typeof ( colorHex ) === "string" ) { localUserData . colorHex = colorHex ; } if ( typeof ( profileImageURL ) === "string" ) { localUserData . profileImageURL = profileImageURL ; if ( localUserData . profileImageURL && localUserData . profileImageURL . length > 0 ) { localUserData . profileImageEl = new Image ( ) ; localUserData . profileImageEl . src = localUserData . profileImageURL ; } else { localUserData . profileImageEl = undefined ; } } if ( typeof ( isAudioInputMuted ) === "boolean" ) { if ( localUserData . isAudioInputMuted && ! isAudioInputMuted ) { playChairSound = true ; } localUserData . isAudioInputMuted = isAudioInputMuted ; } if ( typeof ( echoCancellationAvailable ) === "boolean" ) { localUserData . echoCancellationAvailable = echoCancellationAvailable ; } if ( typeof ( echoCancellationEnabled ) === "boolean" ) { localUserData . echoCancellationEnabled = echoCancellationEnabled ; } if ( typeof ( agcAvailable ) === "boolean" ) { localUserData . agcAvailable = agcAvailable ; } if ( typeof ( agcEnabled ) === "boolean" ) { localUserData . agcEnabled = agcEnabled ; } if ( typeof ( noiseSuppressionAvailable ) === "boolean" ) { localUserData . noiseSuppressionAvailable = noiseSuppressionAvailable ; } if ( typeof ( noiseSuppressionEnabled ) === "boolean" ) { localUserData . noiseSuppressionEnabled = noiseSuppressionEnabled ; } if ( typeof ( hiFiGainSliderValue ) === "string" ) { localUserData . hiFiGainSliderValue = hiFiGainSliderValue ; localUserData . hiFiGain = uiController . hiFiGainFromSliderValue ( localUserData . hiFiGainSliderValue ) ; } if ( typeof ( volumeThreshold ) === "number" ) { localUserData . volumeThreshold = volumeThreshold ; } if ( typeof ( currentSeatID ) === "string" ) { if ( ! localUserData . currentSeat || ( localUserData . currentSeat && localUserData . currentSeat . seatID !== currentSeatID ) ) { playChairSound = true ; } if ( localUserData . currentSeat ) { localUserData . currentSeat . occupiedUserData = undefined ; } localUserData . currentSeat = roomController . getSeatFromSeatID ( currentSeatID ) ; if ( localUserData . currentSeat ) { localUserData . currentSeat . occupiedUserData = localUserData ; localUserData . currentRoom = localUserData . currentSeat . room ; } else { localUserData . currentRoom = undefined ; } } if ( typeof ( currentWatchPartyRoomName ) === "string" ) { localUserData . currentWatchPartyRoomName = currentWatchPartyRoomName ; if ( userDataController . myAvatar . myUserData . currentRoom && localUserData . currentWatchPartyRoomName === userDataController . myAvatar . myUserData . currentRoom . name ) { watchPartyController . joinWatchParty ( userDataController . myAvatar . myUserData . currentRoom . name ) ; } } if ( isStreamingVideo !== undefined ) { localUserData . isStreamingVideo = isStreamingVideo ; if ( localUserData . isStreamingVideo !== VideoStreamingStates . SCREENSHARE && uiController . screenShareContainer . querySelector ( `#${ localUserData . providedUserID }` ) ) { uiController . hideScreenShareUI ( ) ; } if ( localUserData . isStreamingVideo !== VideoStreamingStates . NONE && ! videoController . twilioRoom && ! videoController . connectingToTwilio ) { console . log ( "At least one user in this Room is streaming video. Connecting to Twilio..." ) ; videoController . connectToTwilio ( ) ; } else if ( videoController . twilioRoom ) { videoController . maybeDisconnectFromTwilio ( ) ; } } if ( playChairSound ) { howlerController . playSound ( { src : chairSounds [ Math . floor ( Math . random ( ) * chairSounds . length ) ] , randomSoundRate : true , positionM : localUserData . positionCurrent , volume : 0.3 , tag : "environment" } ) ; } console . log ( `Updated participant:\nVisit ID Hash \`${ localUserData . visitIDHash }\`:\nDisplay Name: \`${ displayName }\`\nColor: ${ colorHex }\nprofileImageURL: ${ profileImageURL }\nisAudioInputMuted: ${ isAudioInputMuted }\nCurrent Seat ID: ${ localUserData . currentSeat ? localUserData . currentSeat . seatID : "undefined" }\nCurrent Room Name: ${ localUserData . currentRoom ? localUserData . currentRoom . name : "undefined" }\nechoCancellationEnabled: ${ localUserData . echoCancellationEnabled }\nagcEnabled: ${ localUserData . agcEnabled }\nnsEnabled: ${ localUserData . noiseSuppressionEnabled }\nhiFiGainSliderValue: ${ localUserData . hiFiGainSliderValue }\nvolumeThreshold:${ localUserData . volumeThreshold }\ncurrentWatchPartyRoomName:${ localUserData . currentWatchPartyRoomName }\nisStreamingVideo:${ localUserData . isStreamingVideo }\n` ) ; } else if ( visitIDHash && displayName ) { localUserData = { visitIDHash , displayName , colorHex , profileImageURL , isAudioInputMuted , echoCancellationAvailable , echoCancellationEnabled , agcAvailable , agcEnabled , noiseSuppressionAvailable , noiseSuppressionEnabled , hiFiGainSliderValue , volumeThreshold , currentWatchPartyRoomName , tempData : { } , isStreamingVideo , } ; localUserData . hiFiGain = uiController . hiFiGainFromSliderValue ( localUserData . hiFiGainSliderValue ) ; if ( localUserData . currentSeat ) { localUserData . currentSeat . occupiedUserData = undefined ; } localUserData . currentSeat = roomController . getSeatFromSeatID ( currentSeatID ) ; if ( localUserData . currentSeat ) { localUserData . currentSeat . occupiedUserData = localUserData ; localUserData . currentRoom = localUserData . currentSeat . room ; } else { localUserData . currentRoom = undefined ; } if ( localUserData . profileImageURL && localUserData . profileImageURL . length > 0 ) { localUserData . profileImageEl = new Image ( ) ; localUserData . profileImageEl . src = localUserData . profileImageURL ; } else { localUserData . profileImageEl = undefined ; } userDataController . allOtherUserData . push ( localUserData ) ; } uiController . maybeUpdateAvatarContextMenu ( localUserData ) ; roomController . updateRoomList ( ) ; } ) ; if ( ! this . retrievedInitialWebSocketServerData ) { this . retrievedInitialWebSocketServerData = true ; if ( connectionController . receivedInitialOtherUserDataFromHiFi && roomController . roomsInitialized ) { userDataController . myAvatar . positionSelfInRoom ( roomController . getStartingRoomName ( ) ) ; } } } ) ; this . socket . on ( "onRequestToEnableEchoCancellation" , ( { fromVisitIDHash } ) => { console . warn ( `Got a request from \`${ fromVisitIDHash }\` to enable echo cancellation!` ) ; if ( typeof ( navigator ) !== "undefined" && typeof ( navigator . mediaDevices ) !== "undefined" && typeof ( navigator . mediaDevices . getSupportedConstraints ) !== "undefined" && navigator . mediaDevices . getSupportedConstraints ( ) . echoCancellation ) { avDevicesController . audioConstraints . echoCancellation = true ; connectionController . setNewInputAudioMediaStream ( ) ; } else { console . warn ( "Can't enable echoCancellation; current browser doesn't support that constraint!" ) ; } } ) ; this . socket . on ( "onRequestToDisableEchoCancellation" , ( { fromVisitIDHash } ) => { console . warn ( `Got a request from \`${ fromVisitIDHash }\` to disable echo cancellation!` ) ; avDevicesController . audioConstraints . echoCancellation = false ; connectionController . setNewInputAudioMediaStream ( ) ; } ) ; this . socket . on ( "onRequestToEnableAGC" , ( { fromVisitIDHash } ) => { console . warn ( `Got a request from \`${ fromVisitIDHash }\` to enable AGC!` ) ; let supportedConstraints = navigator ?. mediaDevices ?. getSupportedConstraints ( ) ; if ( typeof ( navigator ) !== "undefined" && typeof ( navigator . mediaDevices ) !== "undefined" && typeof ( navigator . mediaDevices . getSupportedConstraints ) !== "undefined" && supportedConstraints . autoGainControl ) { avDevicesController . audioConstraints . autoGainControl = true ; connectionController . setNewInputAudioMediaStream ( ) ; } else { console . warn ( "Can't enable autoGainControl; current browser doesn't support that constraint!" ) ; } } ) ; this . socket . on ( "onRequestToDisableAGC" , ( { fromVisitIDHash } ) => { console . warn ( `Got a request from \`${ fromVisitIDHash }\` to disable AGC!` ) ; avDevicesController . audioConstraints . autoGainControl = false ; connectionController . setNewInputAudioMediaStream ( ) ; } ) ; this . socket . on ( "onRequestToEnableNoiseSuppression" , ( { fromVisitIDHash } ) => { console . warn ( `Got a request from \`${ fromVisitIDHash }\` to enable Noise Suppression!` ) ; let supportedConstraints = navigator ?. mediaDevices ?. getSupportedConstraints ( ) ; if ( typeof ( navigator ) !== "undefined" && typeof ( navigator . mediaDevices ) !== "undefined" && typeof ( navigator . mediaDevices . getSupportedConstraints ) !== "undefined" && supportedConstraints . noiseSuppression ) { avDevicesController . audioConstraints . noiseSuppression = true ; connectionController . setNewInputAudioMediaStream ( ) ; } else { console . warn ( "Can't enable noiseSuppression; current browser doesn't support that constraint!" ) ; } } ) ; this . socket . on ( "onRequestToDisableNoiseSuppression" , ( { fromVisitIDHash } ) => { console . warn ( `Got a request from \`${ fromVisitIDHash }\` to disable NS!` ) ; avDevicesController . audioConstraints . noiseSuppression = false ; connectionController . setNewInputAudioMediaStream ( ) ; } ) ; this . socket . on ( "onRequestToChangeHiFiGainSliderValue" , ( { fromVisitIDHash , newHiFiGainSliderValue } ) => { console . warn ( `Got a request from \`${ fromVisitIDHash }\` to change HiFiGainSliderValue to \`${ newHiFiGainSliderValue }\`!` ) ; userInputController . setHiFiGainFromSliderValue ( newHiFiGainSliderValue ) ; } ) ; this . socket . on ( "onRequestToChangeVolumeThreshold" , ( { fromVisitIDHash , newVolumeThreshold } ) => { console . warn ( `Got a request from \`${ fromVisitIDHash }\` to change Volume Threshold to \`${ newVolumeThreshold }\`!` ) ; userInputController . setVolumeThreshold ( newVolumeThreshold ) ; } ) ; this . socket . on ( "onRequestToMuteAudioInputDevice" , ( { fromVisitIDHash } ) => { console . warn ( `Got a request from \`${ fromVisitIDHash }\` to mute audio input device!` ) ; userInputController . setInputMute ( true ) ; } ) ; this . socket . on ( "requestParticleAdd" , ( { visitIDHash , particleData } ) => { let particleParams = JSON . parse ( particleData ) ; if ( particleParams . name ) { console . log ( `"${ visitIDHash }" added a signal particle!` ) ; signalsController . addSignal ( particleParams ) ; } } ) ; this . socket . on ( "requestSoundAdd" , ( { visitIDHash , soundParams } ) => { let parsedSoundParams = JSON . parse ( soundParams ) ; console . log ( `"${ visitIDHash }" requested to play a sound!` ) ; howlerController . playSound ( parsedSoundParams ) ; } ) ; } maybeSendInitialWebSocketData ( ) { if ( ! this . readyToSendWebSocketData ) { return ; } const myUserData = userDataController . myAvatar . myUserData ; let userUUID = localStorage . getItem ( 'userUUID' ) ; if ( ! userUUID ) { userUUID = Utilities . generateUUID ( true ) ; localStorage . setItem ( 'userUUID' , userUUID ) ; uiController . showFTUE ( ) ; } else { uiController . showMainUI ( ) ; } this . socket . emit ( "addParticipant" , { userUUID : userUUID , sessionStartTimestamp : Date . now ( ) , spaceName : HIFI_SPACE_NAME , visitIDHash : myUserData . visitIDHash , currentSeatID : myUserData . currentSeat ? myUserData . currentSeat . seatID : "" , displayName : myUserData . displayName , colorHex : myUserData . colorHex , profileImageURL : myUserData . profileImageURL , isAudioInputMuted : myUserData . isAudioInputMuted , echoCancellationAvailable : myUserData . echoCancellationAvailable , echoCancellationEnabled : myUserData . echoCancellationEnabled , agcAvailable : myUserData . agcAvailable , agcEnabled : myUserData . agcEnabled , noiseSuppressionAvailable : myUserData . noiseSuppressionAvailable , noiseSuppressionEnabled : myUserData . noiseSuppressionEnabled , hiFiGainSliderValue : myUserData . hiFiGainSliderValue , volumeThreshold : myUserData . volumeThreshold , currentWatchPartyRoomName : myUserData . currentWatchPartyRoomName , isStreamingVideo : myUserData . isStreamingVideo , } ) ; } updateMyUserDataOnWebSocketServer ( ) { if ( ! this . readyToSendWebSocketData ) { return ; } const myUserData = userDataController . myAvatar . myUserData ; let dataToUpdate = { spaceName : HIFI_SPACE_NAME , visitIDHash : myUserData . visitIDHash , currentSeatID : myUserData . currentSeat ? myUserData . currentSeat . seatID : "" , displayName : myUserData . displayName , colorHex : myUserData . colorHex , profileImageURL : myUserData . profileImageURL , isAudioInputMuted : myUserData . isAudioInputMuted , echoCancellationAvailable : myUserData . echoCancellationAvailable , echoCancellationEnabled : myUserData . echoCancellationEnabled , agcAvailable : myUserData . agcAvailable , agcEnabled : myUserData . agcEnabled , noiseSuppressionAvailable : myUserData . noiseSuppressionAvailable , noiseSuppressionEnabled : myUserData . noiseSuppressionEnabled , hiFiGainSliderValue : myUserData . hiFiGainSliderValue , volumeThreshold : myUserData . volumeThreshold , currentWatchPartyRoomName : myUserData . currentWatchPartyRoomName , isStreamingVideo : myUserData . isStreamingVideo , } ; console . log ( `Updating data about me on server:\n${ JSON . stringify ( dataToUpdate ) }` ) ; this . socket . emit ( "editParticipant" , dataToUpdate ) ; } requestToEnableEchoCancellation ( visitIDHash ) { this . socket . emit ( "requestToEnableEchoCancellation" , { spaceName : HIFI_SPACE_NAME , toVisitIDHash : visitIDHash , fromVisitIDHash : userDataController . myAvatar . myUserData . visitIDHash } ) ; } requestToDisableEchoCancellation ( visitIDHash ) { this . socket . emit ( "requestToDisableEchoCancellation" , { spaceName : HIFI_SPACE_NAME , toVisitIDHash : visitIDHash , fromVisitIDHash : userDataController . myAvatar . myUserData . visitIDHash } ) ; } requestToEnableAGC ( visitIDHash ) { this . socket . emit ( "requestToEnableAGC" , { spaceName : HIFI_SPACE_NAME , toVisitIDHash : visitIDHash , fromVisitIDHash : userDataController . myAvatar . myUserData . visitIDHash } ) ; } requestToDisableAGC ( visitIDHash ) { this . socket . emit ( "requestToDisableAGC" , { spaceName : HIFI_SPACE_NAME , toVisitIDHash : visitIDHash , fromVisitIDHash : userDataController . myAvatar . myUserData . visitIDHash } ) ; } requestToEnableNoiseSuppression ( visitIDHash ) { this . socket . emit ( "requestToEnableNoiseSuppression" , { spaceName : HIFI_SPACE_NAME , toVisitIDHash : visitIDHash , fromVisitIDHash : userDataController . myAvatar . myUserData . visitIDHash } ) ; } requestToDisableNoiseSuppression ( visitIDHash ) { this . socket . emit ( "requestToDisableNoiseSuppression" , { spaceName : HIFI_SPACE_NAME , toVisitIDHash : visitIDHash , fromVisitIDHash : userDataController . myAvatar . myUserData . visitIDHash } ) ; } requestToChangeHiFiGainSliderValue ( visitIDHash , newHiFiGainSliderValue ) { this . socket . emit ( "requestToChangeHiFiGainSliderValue" , { spaceName : HIFI_SPACE_NAME , toVisitIDHash : visitIDHash , fromVisitIDHash : userDataController . myAvatar . myUserData . visitIDHash , newHiFiGainSliderValue } ) ; } requestToChangeVolumeThreshold ( visitIDHash , newVolumeThreshold ) { this . socket . emit ( "requestToChangeVolumeThreshold" , { spaceName : HIFI_SPACE_NAME , toVisitIDHash : visitIDHash , fromVisitIDHash : userDataController . myAvatar . myUserData . visitIDHash , newVolumeThreshold } ) ; } requestToMuteAudioInputDevice ( visitIDHash ) { this . socket . emit ( "requestToMuteAudioInputDevice" , { spaceName : HIFI_SPACE_NAME , toVisitIDHash : visitIDHash , fromVisitIDHash : userDataController . myAvatar . myUserData . visitIDHash } ) ; } stopWebSocketStuff ( ) { console . log ( `Disconnecting from WebSocket server...` ) ; this . socket . disconnect ( ) ; this . readyToSendWebSocketData = false ; } }