import { useCallback , useEffect , useState } from "react" ; import Swal from "sweetalert2" ; import { ClientGameLibrary , ClientParty } from "../types/types" ; import { logEvent } from "./analytics" ; import { io } from "socket.io-client" ; import { RocketcrabDexie } from "./dexie" ; import { setCookie as setNookie } from "nookies" ; import { NextRouter } from "next/router" ; import { SocketEvent } from "../types/enums" ; const socket = io ( ) ; export const useRocketcrabClientSocket = ( { code , router , cookiePartyState , isReconnect , gameLibrary , } ) : UseRocketcrabClientSocketReturn => { const [ partyState , setPartyState ] = useState < ClientParty | undefined > ( ) ; const [ socketConnected , setSocketConnected ] = useState ( socket ?. connected ) ; const [ showReconnecting , setShowReconnecting ] = useState ( false ) ; const { me , playerList , selectedGameId } = partyState || { } ; const selectedGame = gameLibrary ?. gameList ?. find ( ( { id } ) => id === selectedGameId ) ; useEffect ( ( ) => { socket . open ( ) ; socket . on ( SocketEvent . CONNECT , ( ) => setSocketConnected ( true ) ) ; socket . on ( SocketEvent . UPDATE , ( newPartyState ) => { setPartyState ( newPartyState ) ; setShowReconnecting ( false ) ; if ( Number . isInteger ( newPartyState . me . id ) ) { setCookie ( "lastPartyState" , JSON . stringify ( newPartyState ) ) ; } } ) ; socket . on ( SocketEvent . INVALID_NAME , ( ) => { if ( code === "ffff" ) return ; Swal . fire ( { title : "Try again" , text : "Name already in use" , icon : "error" , heightAuto : false , } ) ; } ) ; socket . on ( SocketEvent . DISCONNECT , ( reason ) => { if ( reason === "io server disconnect" ) { router . push ( "/" ) ; } logEvent ( "common-reconnecting" ) ; setShowReconnecting ( true ) ; } ) ; return ( ) => { socket . close ( ) ; setSocketConnected ( false ) ; setPartyState ( undefined ) ; } ; } , [ ] ) ; useEffect ( ( ) => { if ( ! socketConnected ) return ; if ( code && ! partyState ) { joinParty ( code , cookiePartyState , isReconnect ) ; } socket . io . on ( SocketEvent . RECONNECT , ( ) => { joinParty ( code , partyState || cookiePartyState , true ) ; setShowReconnecting ( false ) ; } ) ; socket . on ( SocketEvent . INVALID_PARTY , ( ) => { router . push ( "/join?invalid=" + code ) ; } ) ; return ( ) => { socket . io . off ( SocketEvent . RECONNECT ) ; socket . off ( SocketEvent . INVALID_PARTY ) ; } ; } , [ code , partyState , isReconnect , socketConnected ] ) ; const onNameEntry = useCallback ( ( enteredName ) => { socket . emit ( SocketEvent . NAME , enteredName ) ; } , [ ] ) ; const onSelectGame = useCallback ( ( gameId ) => { socket . emit ( SocketEvent . GAME_SELECT , gameId ) ; } , [ ] ) ; const onStartGame = useCallback ( ( gameId ? ) => { const { minPlayers , maxPlayers , name } = selectedGame ; if ( minPlayers && playerList . length < minPlayers ) { const morePlayers = minPlayers - playerList . length ; Swal . fire ( { title : "Not enough players!" , text : `${ name } requires at least ${ minPlayers } player${ minPlayers === 1 ? "" : "s" }. You need ${ morePlayers } more player${ morePlayers === 1 ? "" : "s" }.` , icon : "error" , heightAuto : false , } ) ; return ; } if ( maxPlayers && playerList . length > maxPlayers ) { const lessPlayers = playerList . length - minPlayers ; Swal . fire ( { title : "Too many players!" , text : `${ name } has a maximum of ${ minPlayers } player${ minPlayers === 1 ? "" : "s" }. You have ${ lessPlayers } too many players.` , icon : "error" , heightAuto : false , } ) ; return ; } socket . emit ( SocketEvent . GAME_START , gameId ) ; const db = new RocketcrabDexie ( ) ; db . addGame ( selectedGameId ) ; if ( me ?. isHost ) { logEvent ( "party-numberOfPlayers" , playerList . length . toString ( ) ) ; logEvent ( "party-game" , selectedGameId ) ; logEvent ( "party-isPublic" , ( ! ! partyState . publicEndDate ) . toString ( ) ) ; logEvent ( "party-mode" , partyState . mode ) ; } } , [ playerList , selectedGameId ] ) ; const onExitGame = useCallback ( ( ) => { socket . emit ( SocketEvent . GAME_EXIT ) ; } , [ ] ) ; const onHostGameLoaded = useCallback ( ( ) => setTimeout ( ( ) => socket . emit ( SocketEvent . HOST_GAME_LOADED ) , 2000 ) , [ ] ) ; const onSendChat = useCallback ( ( message ) => { socket . emit ( SocketEvent . CHAT_MESSAGE , message ) ; logEvent ( "common-sendChat" ) ; } , [ ] ) ; const onKick = useCallback ( ( playerId , name ) => { Swal . fire ( { title : `Kick ${ name }?` , showCancelButton : true , confirmButtonText : `Kick player` , icon : "warning" , heightAuto : false , } ) . then ( ( { isConfirmed } ) => { if ( isConfirmed ) { Swal . fire ( { title : `Ban ${ name } as well?` , text : "This may prevent anyone else on the same network as this player from joining as well. If you want to let them join again, you'll have to make a new party." , showCancelButton : true , confirmButtonText : `Kick & ban player` , cancelButtonText : "Just kick" , icon : "warning" , heightAuto : false , } ) . then ( ( { isConfirmed } ) => { if ( isConfirmed ) { socket . emit ( SocketEvent . KICK_PLAYER , { playerId , isBan : true , } ) ; Swal . fire ( { title : "Kicked & banned!" , text : "Good riddance!" , icon : "success" , heightAuto : false , } ) ; logEvent ( "common-ban" ) ; } else { socket . emit ( SocketEvent . KICK_PLAYER , { playerId , isBan : false , } ) ; Swal . fire ( { title : "Kicked!" , text : "Bye bye!" , icon : "success" , heightAuto : false , } ) ; logEvent ( "common-kick" ) ; } } ) ; } } ) ; } , [ ] ) ; const onSetIsPublic = useCallback ( ( proposedIsPublic ) => { socket . emit ( SocketEvent . SET_IS_PUBLIC , proposedIsPublic ) ; } , [ ] ) ; return { partyState , onNameEntry , onSelectGame , onStartGame , onExitGame , onHostGameLoaded , showReconnecting , onSendChat , onKick , onSetIsPublic , } ; } ; const joinParty = ( code , partyState , reconnecting ) => { if ( code === "ffff" ) { const randFourDig = Math . floor ( 1000 + Math . random ( ) * 9000 ) ; partyState . me . name = String ( randFourDig ) ; } socket . emit ( SocketEvent . JOIN_PARTY , { code , lastPartyState : { ... partyState , playerList : undefined } , reconnecting , } ) ; } ; const setCookie = ( key , value ) => setNookie ( null , key , value , { maxAge : 2147483647 , } ) ; type UseRocketcrabClientSocketProps = { code : string ; router : NextRouter ; cookiePartyState ? : ClientParty ; isReconnect : boolean ; gameLibrary : ClientGameLibrary ; } ; type UseRocketcrabClientSocketReturn = { partyState : ClientParty ; onNameEntry : ( enteredName : string ) => void ; onSelectGame : ( gameId : string ) => void ; onStartGame : ( gameId ? : string ) => void ; onExitGame : ( ) => void ; onHostGameLoaded : ( ) => void ; showReconnecting : boolean ; onSendChat : ( message : string ) => void ; onKick : ( id : number , name : string ) => void ; onSetIsPublic : ( isPublic : boolean ) => void ; } ;