192 lines
5.3 KiB
TypeScript
192 lines
5.3 KiB
TypeScript
import { socket } from "@lib/socket"
|
|
import { GamePropsSchema } from "@lib/zodSchemas"
|
|
import status from "http-status"
|
|
import { useRouter } from "next/router"
|
|
import { useEffect, useMemo, useState } from "react"
|
|
import { toast } from "react-toastify"
|
|
import { GameSettings, PlayerEvent } from "../interfaces/frontend"
|
|
import { isAuthenticated } from "../pages/start"
|
|
import { useGameProps } from "./useGameProps"
|
|
import useIndex from "./useIndex"
|
|
|
|
/** This function should only be called once per page, otherwise there will be multiple socket connections and duplicate event listeners. */
|
|
function useSocket() {
|
|
const [isConnectedState, setIsConnectedState] = useState(false)
|
|
const { selfIndex } = useIndex()
|
|
const {
|
|
payload,
|
|
userStates,
|
|
setPlayer,
|
|
setSetting,
|
|
full,
|
|
setIsReady,
|
|
gameState,
|
|
setIsConnected,
|
|
setActiveIndex,
|
|
DispatchMove,
|
|
setShips,
|
|
} = useGameProps()
|
|
const router = useRouter()
|
|
|
|
const isConnected = useMemo(
|
|
() =>
|
|
selfIndex >= 0 ? userStates[selfIndex].isConnected : isConnectedState,
|
|
[selfIndex, isConnectedState, userStates],
|
|
)
|
|
|
|
useEffect(() => {
|
|
if (selfIndex < 0) return
|
|
setIsConnected({
|
|
i: selfIndex,
|
|
isConnected: isConnectedState,
|
|
})
|
|
}, [selfIndex, isConnectedState, setIsConnected])
|
|
|
|
useEffect(() => {
|
|
const connect = () => {
|
|
console.log("connected")
|
|
toast.dismiss("connect_error")
|
|
setIsConnectedState(true)
|
|
}
|
|
|
|
const connectError = (error: Error) => {
|
|
console.log("Connection error:", error.message)
|
|
if (error.message === status["403"]) router.push("/")
|
|
if (error.message !== "xhr poll error") return
|
|
const toastId = "connect_error"
|
|
const isActive = toast.isActive(toastId)
|
|
console.log(toastId, isActive)
|
|
if (isActive)
|
|
toast.update(toastId, {
|
|
autoClose: 5000,
|
|
})
|
|
else
|
|
toast.warn("Es gibt Probleme mit der Echtzeitverbindung.", { toastId })
|
|
}
|
|
|
|
const playerEvent = (event: PlayerEvent) => {
|
|
const { type, i } = event
|
|
let message: string
|
|
console.log("playerEvent", type)
|
|
switch (type) {
|
|
case "disconnect":
|
|
setIsConnected({
|
|
i,
|
|
isConnected: false,
|
|
})
|
|
message = "Player is disconnected."
|
|
break
|
|
|
|
case "leave":
|
|
message = "Player has left the lobby."
|
|
break
|
|
|
|
case "connect":
|
|
setIsConnected({
|
|
i,
|
|
isConnected: true,
|
|
})
|
|
socket.emit("isReady", userStates[selfIndex].isReady)
|
|
message = "Player has joined the lobby."
|
|
break
|
|
|
|
default:
|
|
message = "Not defined yet."
|
|
break
|
|
}
|
|
toast.info(message, { toastId: message })
|
|
if (type === "disconnect") return
|
|
const { payload, hash } = event
|
|
const newHash = setPlayer(payload)
|
|
console.log(newHash, hash, !newHash, newHash === hash)
|
|
if (!newHash || newHash === hash) return
|
|
console.log("hash", hash, newHash)
|
|
socket.emit("update", (body) => {
|
|
console.log("update")
|
|
full(body)
|
|
})
|
|
}
|
|
|
|
const gameSetting = (payload: GameSettings, hash: string) => {
|
|
const newHash = setSetting(payload)
|
|
if (!newHash || newHash === hash) return
|
|
console.log("hash", hash, newHash)
|
|
socket.emit("update", (body) => {
|
|
console.log("update")
|
|
full(body)
|
|
})
|
|
}
|
|
|
|
const activeIndex = (i: number) => setActiveIndex(i, selfIndex)
|
|
|
|
const disconnect = () => {
|
|
console.log("disconnect")
|
|
setIsConnectedState(false)
|
|
}
|
|
|
|
socket.on("connect", connect)
|
|
socket.on("connect_error", connectError)
|
|
socket.on("gameSetting", gameSetting)
|
|
socket.on("playerEvent", playerEvent)
|
|
socket.on("isReady", setIsReady)
|
|
socket.on("gameState", gameState)
|
|
socket.on("dispatchMove", DispatchMove)
|
|
socket.on("activeIndex", activeIndex)
|
|
socket.on("ships", setShips)
|
|
socket.on("disconnect", disconnect)
|
|
|
|
return () => {
|
|
socket.off("connect", connect)
|
|
socket.off("connect_error", connectError)
|
|
socket.off("gameSetting", gameSetting)
|
|
socket.off("playerEvent", playerEvent)
|
|
socket.off("isReady", setIsReady)
|
|
socket.off("gameState", gameState)
|
|
socket.off("dispatchMove", DispatchMove)
|
|
socket.off("activeIndex", activeIndex)
|
|
socket.off("ships", setShips)
|
|
socket.off("disconnect", disconnect)
|
|
}
|
|
}, [
|
|
DispatchMove,
|
|
full,
|
|
gameState,
|
|
router,
|
|
selfIndex,
|
|
setActiveIndex,
|
|
setIsConnected,
|
|
setIsReady,
|
|
setPlayer,
|
|
setSetting,
|
|
setShips,
|
|
userStates,
|
|
])
|
|
|
|
useEffect(() => {
|
|
if (!payload?.game?.id) {
|
|
socket.disconnect()
|
|
fetch("/api/game/running", {
|
|
method: "GET",
|
|
})
|
|
.then(isAuthenticated)
|
|
.then((game) => GamePropsSchema.parse(game))
|
|
.then((res) => full(res))
|
|
.catch((e) => console.log(e))
|
|
return
|
|
}
|
|
if (isConnected) return
|
|
socket.connect()
|
|
const start = Date.now()
|
|
socket.volatile.emit("ping", () => {
|
|
const duration = Date.now() - start
|
|
console.log("ping", duration)
|
|
})
|
|
}, [full, isConnected, payload?.game?.id])
|
|
|
|
return {
|
|
isConnected:
|
|
selfIndex >= 0 ? userStates[selfIndex].isConnected : isConnectedState,
|
|
}
|
|
}
|
|
|
|
export default useSocket
|