Improved handling ws connection and redirect
This commit is contained in:
parent
53a07b21b0
commit
0317a3343c
10 changed files with 100 additions and 50 deletions
|
@ -47,13 +47,13 @@ function LobbyFrame({ openSettings }: { openSettings: () => void }) {
|
|||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!launching || launchTime >= 1) return
|
||||
router.push("/gamefield")
|
||||
if (!launching || launchTime > 0) return
|
||||
socket.emit("starting")
|
||||
}, [launching, launchTime, router])
|
||||
|
||||
useEffect(() => {
|
||||
if (!launching) return setLaunchTime(3)
|
||||
if (launchTime === 0) return
|
||||
if (launchTime < 0) return
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
setLaunchTime((e) => e - 1)
|
||||
|
@ -67,16 +67,29 @@ function LobbyFrame({ openSettings }: { openSettings: () => void }) {
|
|||
socket.emit("update", full)
|
||||
}, [full, payload?.game?.id, isConnected])
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
typeof payload?.game?.state !== "string" ||
|
||||
payload?.game?.state === "lobby"
|
||||
)
|
||||
return
|
||||
router.push("gamefield")
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="mx-32 flex flex-col self-stretch rounded-3xl bg-gray-400">
|
||||
<div className="flex items-center justify-between border-b-2 border-slate-900">
|
||||
<Icon src="speech_bubble.png">Chat</Icon>
|
||||
<h1 className="font-farro text-5xl font-medium">
|
||||
{launching ? (
|
||||
<WithDots>{"Game is starting in " + launchTime}</WithDots>
|
||||
<WithDots>
|
||||
{launchTime < 0
|
||||
? "Game starts"
|
||||
: "Game is starting in " + launchTime}
|
||||
</WithDots>
|
||||
) : (
|
||||
<>
|
||||
Game-PIN:{" "}
|
||||
{"Game-PIN: "}
|
||||
{isConnected ? (
|
||||
<span className="underline">{payload?.gamePin ?? "----"}</span>
|
||||
) : (
|
||||
|
@ -114,8 +127,8 @@ function LobbyFrame({ openSettings }: { openSettings: () => void }) {
|
|||
disabled={launching}
|
||||
onClick={() => {
|
||||
leave(async () => {
|
||||
await router.push("/")
|
||||
reset()
|
||||
await router.push("/")
|
||||
})
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Draw, DrawLineProps, Point } from "../interfaces/frontend"
|
||||
import { Draw, Point } from "../interfaces/frontend"
|
||||
import { useDrawProps } from "./useDrawProps"
|
||||
import { socket } from "@lib/socket"
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
|
@ -108,7 +108,7 @@ export const useDraw = () => {
|
|||
drawLine({ prevPoint, currentPoint, ctx, color })
|
||||
})
|
||||
|
||||
socket.on("clear", clear)
|
||||
socket.on("canvas-clear", clear)
|
||||
|
||||
return () => {
|
||||
socket.removeAllListeners()
|
||||
|
|
|
@ -45,6 +45,7 @@ export type Action = {
|
|||
full: (newProps: GamePropsSchema) => void
|
||||
leave: (cb: () => void) => void
|
||||
setIsReady: (payload: { i: number; isReady: boolean }) => void
|
||||
starting: () => void
|
||||
setIsConnected: (payload: { i: number; isConnected: boolean }) => void
|
||||
reset: () => void
|
||||
}
|
||||
|
@ -159,6 +160,17 @@ export const useGameProps = create<State & Action>()(
|
|||
state.userStates[i].isConnected = true
|
||||
})
|
||||
),
|
||||
starting: () =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
if (state.payload?.game?.state !== "lobby") return
|
||||
state.payload.game.state = "starting"
|
||||
state.userStates = state.userStates.map((e) => ({
|
||||
...e,
|
||||
isReady: false,
|
||||
}))
|
||||
})
|
||||
),
|
||||
setIsConnected: ({ i, isConnected }) =>
|
||||
set(
|
||||
produce((state: State) => {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { isAuthenticated } from "../pages/start"
|
||||
import { useGameProps } from "./useGameProps"
|
||||
import { socket } from "@lib/socket"
|
||||
import { GamePropsSchema } from "@lib/zodSchemas"
|
||||
import status from "http-status"
|
||||
import { useSession } from "next-auth/react"
|
||||
import { useRouter } from "next/router"
|
||||
|
@ -16,6 +18,7 @@ function useSocket() {
|
|||
setSetting,
|
||||
full,
|
||||
setIsReady,
|
||||
starting,
|
||||
setIsConnected,
|
||||
} = useGameProps()
|
||||
const { data: session } = useSession()
|
||||
|
@ -27,6 +30,10 @@ function useSocket() {
|
|||
if (!isIndex) return { i: undefined, isIndex }
|
||||
return { i, isIndex }
|
||||
}, [payload?.users, session?.user?.id])
|
||||
const isConnected = useMemo(
|
||||
() => (isIndex ? userStates[i].isConnected : isConnectedState),
|
||||
[i, isConnectedState, isIndex, userStates]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isIndex) return
|
||||
|
@ -37,8 +44,6 @@ function useSocket() {
|
|||
}, [i, isConnectedState, isIndex, setIsConnected])
|
||||
|
||||
useEffect(() => {
|
||||
if (!session?.user.id) return
|
||||
|
||||
socket.on("connect", () => {
|
||||
console.log("connected")
|
||||
toast.dismiss("connect_error")
|
||||
|
@ -73,7 +78,7 @@ function useSocket() {
|
|||
socket.on("playerEvent", (event) => {
|
||||
const { type, i } = event
|
||||
let message: string
|
||||
console.log(type)
|
||||
console.log("playerEvent", type)
|
||||
switch (type) {
|
||||
case "disconnect":
|
||||
setIsConnected({
|
||||
|
@ -115,6 +120,8 @@ function useSocket() {
|
|||
|
||||
socket.on("isReady", setIsReady)
|
||||
|
||||
socket.on("starting", starting)
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
console.log("disconnect")
|
||||
setIsConnectedState(false)
|
||||
|
@ -125,27 +132,37 @@ function useSocket() {
|
|||
}
|
||||
}, [
|
||||
full,
|
||||
payload?.game?.id,
|
||||
router,
|
||||
session?.user.id,
|
||||
setIsConnected,
|
||||
setIsReady,
|
||||
setPlayer,
|
||||
setSetting,
|
||||
starting,
|
||||
userStates,
|
||||
])
|
||||
|
||||
// useEffect(() => {
|
||||
// if (!isConnected) return
|
||||
// let count = 0
|
||||
// const interval = setInterval(() => {
|
||||
// const start = Date.now()
|
||||
// socket.volatile.emit("ping", ++count, (count) => {
|
||||
// const duration = Date.now() - start
|
||||
// console.log("ping", count, duration)
|
||||
// })
|
||||
// }, 5000)
|
||||
// return () => clearInterval(interval)
|
||||
// }, [isConnected])
|
||||
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()
|
||||
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: isIndex ? userStates[i].isConnected : isConnectedState }
|
||||
}
|
||||
|
|
|
@ -47,20 +47,22 @@ export interface ServerToClientEvents {
|
|||
"get-canvas-state": () => void
|
||||
"canvas-state-from-server": (state: string, userIndex: number) => void
|
||||
"draw-line": (props: DrawLineProps, userIndex: number) => void
|
||||
clear: () => void
|
||||
"canvas-clear": () => void
|
||||
starting: () => void
|
||||
}
|
||||
|
||||
export interface ClientToServerEvents {
|
||||
update: (callback: (game: GamePropsSchema) => void) => void
|
||||
isReady: (isReady: boolean) => void
|
||||
isConnected: (isReady: boolean) => void
|
||||
ping: (count: number, callback: (count: number) => void) => void
|
||||
ping: (callback: () => void) => void
|
||||
join: (withAck: (ack: boolean) => void) => void
|
||||
gameSetting: (payload: GameSettings, callback: (hash: string) => void) => void
|
||||
leave: (withAck: (ack: boolean) => void) => void
|
||||
"canvas-state": (state: string) => void
|
||||
"draw-line": (props: DrawLineProps) => void
|
||||
clear: () => void
|
||||
"canvas-clear": () => void
|
||||
starting: () => void
|
||||
}
|
||||
|
||||
interface InterServerEvents {
|
||||
|
|
|
@ -3,4 +3,5 @@ import { io } from "socket.io-client"
|
|||
|
||||
export const socket: cSocket = io({
|
||||
path: "/api/ws",
|
||||
autoConnect: false,
|
||||
})
|
||||
|
|
|
@ -55,7 +55,7 @@ export default async function join(
|
|||
if (games.length) {
|
||||
return sendResponse(req, res, {
|
||||
message: "Spieler ist bereits in Spiel!",
|
||||
statusCode: 409,
|
||||
redirectUrl: "/api/game/running",
|
||||
type: ["infoCyan"],
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import {
|
|||
NextApiResponseWithSocket,
|
||||
sServer,
|
||||
} from "../../interfaces/NextApiSocket"
|
||||
import { DrawLineProps } from "../../interfaces/frontend"
|
||||
import {
|
||||
composeBody,
|
||||
gameSelects,
|
||||
|
@ -108,9 +107,7 @@ const SocketHandler = async (
|
|||
socket.to(game.id).emit("gameSetting", payload, hash)
|
||||
})
|
||||
|
||||
socket.on("ping", (count, callback) => {
|
||||
callback(count)
|
||||
})
|
||||
socket.on("ping", (callback) => callback())
|
||||
|
||||
socket.on("leave", async (cb) => {
|
||||
if (!socket.data.gameId || !socket.data.user?.id) return cb(false)
|
||||
|
@ -192,23 +189,31 @@ const SocketHandler = async (
|
|||
.emit("canvas-state-from-server", state, socket.data.index)
|
||||
})
|
||||
|
||||
socket.on(
|
||||
"draw-line",
|
||||
({ prevPoint, currentPoint, color }: DrawLineProps) => {
|
||||
if (!socket.data.gameId || !socket.data.index) return
|
||||
socket
|
||||
.to(socket.data.gameId)
|
||||
.emit(
|
||||
"draw-line",
|
||||
{ prevPoint, currentPoint, color },
|
||||
socket.data.index
|
||||
)
|
||||
}
|
||||
)
|
||||
socket.on("draw-line", ({ prevPoint, currentPoint, color }) => {
|
||||
if (!socket.data.gameId || !socket.data.index) return
|
||||
socket
|
||||
.to(socket.data.gameId)
|
||||
.emit(
|
||||
"draw-line",
|
||||
{ prevPoint, currentPoint, color },
|
||||
socket.data.index
|
||||
)
|
||||
})
|
||||
|
||||
socket.on("clear", () => {
|
||||
socket.on("canvas-clear", () => {
|
||||
if (!socket.data.gameId) return
|
||||
socket.to(socket.data.gameId).emit("clear")
|
||||
socket.to(socket.data.gameId).emit("canvas-clear")
|
||||
})
|
||||
|
||||
socket.on("starting", async () => {
|
||||
if (socket.data.index !== 0 || !socket.data.gameId) return
|
||||
await prisma.game.update({
|
||||
where: { id: socket.data.gameId },
|
||||
data: {
|
||||
state: "starting",
|
||||
},
|
||||
})
|
||||
io.to(socket.data.gameId).emit("starting")
|
||||
})
|
||||
|
||||
socket.on("disconnecting", async () => {
|
||||
|
@ -225,7 +230,6 @@ const SocketHandler = async (
|
|||
})
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
// socket.rooms.size === 0
|
||||
logging("Disconnect: " + socket.id, ["debug"], socket.request)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -32,7 +32,7 @@ export const User_GameScalarFieldEnumSchema = z.enum(['id','createdAt','gameId',
|
|||
|
||||
export const VerificationTokenScalarFieldEnumSchema = z.enum(['identifier','token','expires']);
|
||||
|
||||
export const GameStateSchema = z.enum(['launching','running','ended']);
|
||||
export const GameStateSchema = z.enum(['lobby','starting','running','ended']);
|
||||
|
||||
export type GameStateType = `${z.infer<typeof GameStateSchema>}`
|
||||
|
||||
|
|
|
@ -69,7 +69,8 @@ model VerificationToken {
|
|||
}
|
||||
|
||||
enum GameState {
|
||||
launching
|
||||
lobby
|
||||
starting
|
||||
running
|
||||
ended
|
||||
}
|
||||
|
@ -78,7 +79,7 @@ model Game {
|
|||
id String @id @default(cuid())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
state GameState @default(launching)
|
||||
state GameState @default(lobby)
|
||||
allowSpectators Boolean @default(true)
|
||||
allowSpecials Boolean @default(true)
|
||||
allowChat Boolean @default(true)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue