diff --git a/leaky-ships/components/Lobby/LobbyFrame.tsx b/leaky-ships/components/Lobby/LobbyFrame.tsx
index 4c0becf..8ca7d0f 100644
--- a/leaky-ships/components/Lobby/LobbyFrame.tsx
+++ b/leaky-ships/components/Lobby/LobbyFrame.tsx
@@ -1,13 +1,32 @@
import Icon from "./Icon"
import Player from "./Player"
+import { faSpinnerThird } from "@fortawesome/pro-solid-svg-icons"
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import useGameState from "@lib/hooks/useGameState"
import useSocket from "@lib/hooks/useSocket"
+import { useRouter } from "next/router"
import { Fragment, useEffect, useState } from "react"
+import { toast } from "react-toastify"
function LobbyFrame({ openSettings }: { openSettings: () => void }) {
const { payload } = useGameState().gameProps
- useSocket()
- const [dots, setDots] = useState(1)
+ const { isConnected, hasJoined } = useSocket()
+ const [dots, setDots] = useState(3)
+ const router = useRouter()
+
+ useEffect(() => {
+ if (isConnected && hasJoined) return
+ const timeout = setTimeout(() => {
+ toast("Es konnte keine Echtzeitverbindung aufgebaute werden.", {
+ toastId: "connection_error",
+ type: "error",
+ autoClose: 10000,
+ })
+ router.push("/start")
+ }, 15000)
+
+ return () => clearTimeout(timeout)
+ }, [])
useEffect(() => {
if (payload?.player2) return
@@ -21,7 +40,11 @@ function LobbyFrame({ openSettings }: { openSettings: () => void }) {
Chat
Settings
@@ -42,7 +65,8 @@ function LobbyFrame({ openSettings }: { openSettings: () => void }) {
/>
) : (
- Warte auf Spieler 2 {Array.from(Array(dots), () => ".").join("")}
+ Warte auf {isConnected && hasJoined ? "Spieler 2" : "Verbindung"}{" "}
+ {Array.from(Array(dots), () => ".").join("")}
{Array.from(Array(3 - dots), (_, i) => (
))}
diff --git a/leaky-ships/lib/backend/logging.ts b/leaky-ships/lib/backend/logging.ts
index 56d4764..82b491b 100644
--- a/leaky-ships/lib/backend/logging.ts
+++ b/leaky-ships/lib/backend/logging.ts
@@ -1,5 +1,6 @@
import colors, { Color } from "colors"
import fs from "fs"
+import { IncomingMessage } from "http"
import { NextApiRequest } from "next"
colors.enable()
@@ -38,7 +39,7 @@ async function logStartup() {
async function logging(
message: string,
types: Logging[],
- req?: NextApiRequest
+ req?: NextApiRequest | IncomingMessage
) {
if (!started) await logStartup()
const messages = { console: message, file: message }
diff --git a/leaky-ships/lib/hooks/useGameState.tsx b/leaky-ships/lib/hooks/useGameState.tsx
index ff549f8..99f854e 100644
--- a/leaky-ships/lib/hooks/useGameState.tsx
+++ b/leaky-ships/lib/hooks/useGameState.tsx
@@ -3,20 +3,19 @@ import { useSession } from "next-auth/react"
import {
Dispatch,
ReactNode,
- SetStateAction,
createContext,
useContext,
useEffect,
- useState,
useMemo,
+ useReducer,
} from "react"
import { toast } from "react-toastify"
-const initialValue = { payload: null, hash: null }
+const initialValue: GamePropsSchema = { payload: null, hash: null }
interface gameContext {
gameProps: GamePropsSchema
- setGameProps: Dispatch>
+ setGameProps: Dispatch
}
export const gameContext = createContext({
@@ -24,8 +23,30 @@ export const gameContext = createContext({
setGameProps: () => {},
})
+function updateProps(state: GamePropsSchema, body: GamePropsSchema) {
+ if (state.hash === body.hash) {
+ console.log("Everything up to date.")
+ return state
+ } else {
+ console.log("Update was needed.", state.hash, body.hash)
+
+ if (
+ state.payload?.game?.id &&
+ state.payload?.game?.id !== body.payload?.game?.id
+ ) {
+ console.warn(
+ "Different gameId detected on update: ",
+ state.payload?.game?.id,
+ body.payload?.game?.id
+ )
+ }
+
+ return body
+ }
+}
+
export function GameContextProvider({ children }: { children: ReactNode }) {
- const [gameProps, setGameProps] = useState(initialValue)
+ const [gameProps, setGameProps] = useReducer(updateProps, initialValue)
const value = useMemo(
() => ({ gameProps, setGameProps }),
@@ -35,15 +56,10 @@ export function GameContextProvider({ children }: { children: ReactNode }) {
return {children}
}
-function useGameState(initial?: GamePropsSchema) {
+function useGameState() {
const { gameProps, setGameProps } = useContext(gameContext)
const { data: session, status } = useSession()
- useEffect(() => {
- if (!initial) return
- setGameProps(initial)
- }, [initial, setGameProps])
-
useEffect(() => {
if (status === "loading") return
if (!session)
diff --git a/leaky-ships/lib/hooks/useSocket.ts b/leaky-ships/lib/hooks/useSocket.ts
index de8ebcb..0b33951 100644
--- a/leaky-ships/lib/hooks/useSocket.ts
+++ b/leaky-ships/lib/hooks/useSocket.ts
@@ -52,7 +52,7 @@ function useSocket() {
socket?.emit("authenticate", { token: `hello from ${session?.user.email}` })
}, [isConnected, status, session?.user.email])
- return socket
+ return { isConnected, hasJoined }
}
export default useSocket
diff --git a/leaky-ships/lib/socket.ts b/leaky-ships/lib/socket.ts
index 9e71296..bf08521 100644
--- a/leaky-ships/lib/socket.ts
+++ b/leaky-ships/lib/socket.ts
@@ -2,5 +2,5 @@ import { cSocket } from "../interfaces/NextApiSocket"
import { io } from "socket.io-client"
export const socket: cSocket = io({
- path: "/api/ws2",
+ path: "/api/ws",
})
diff --git a/leaky-ships/pages/_app.tsx b/leaky-ships/pages/_app.tsx
index 19b6a0c..f96645b 100644
--- a/leaky-ships/pages/_app.tsx
+++ b/leaky-ships/pages/_app.tsx
@@ -2,6 +2,7 @@ import "../styles/App.scss"
import "../styles/globals.scss"
import "../styles/grid2.scss"
import "../styles/grid.scss"
+import "@fortawesome/fontawesome-svg-core/styles.css"
import { GameContextProvider } from "@lib/hooks/useGameState"
import { SessionProvider } from "next-auth/react"
import type { AppProps } from "next/app"
diff --git a/leaky-ships/pages/api/auth/[...nextauth].ts b/leaky-ships/pages/api/auth/[...nextauth].ts
index aa849b0..10ff3dc 100644
--- a/leaky-ships/pages/api/auth/[...nextauth].ts
+++ b/leaky-ships/pages/api/auth/[...nextauth].ts
@@ -7,17 +7,16 @@ import EmailProvider from "next-auth/providers/email"
import {
uniqueNamesGenerator,
Config,
- adjectives,
animals,
NumberDictionary,
} from "unique-names-generator"
-const numberDictionary = NumberDictionary.generate({ min: 0, max: 999 })
+const numberDictionary = NumberDictionary.generate({ min: 0, max: 9999 })
const customConfig: Config = {
- dictionaries: [adjectives, animals, numberDictionary],
+ dictionaries: [animals, numberDictionary],
separator: " ",
style: "capital",
- length: 3,
+ length: 2,
}
const options: NextAuthOptions = {
diff --git a/leaky-ships/pages/api/game/join.ts b/leaky-ships/pages/api/game/join.ts
index 3ff005f..faab065 100644
--- a/leaky-ships/pages/api/game/join.ts
+++ b/leaky-ships/pages/api/game/join.ts
@@ -4,6 +4,7 @@ import sendError from "@backend/sendError"
import sendResponse from "@backend/sendResponse"
import { rejectionErrors } from "@lib/backend/errors"
import getPinFromBody from "@lib/backend/getPinFromBody"
+import logging from "@lib/backend/logging"
import prisma from "@lib/prisma"
import { GamePropsSchema } from "@lib/zodSchemas"
import type { NextApiRequest, NextApiResponse } from "next"
@@ -57,7 +58,11 @@ export default async function join(
type: ["debug", "infoCyan"],
})
} catch (err: any) {
- console.log("HERE".red, err.code, err.meta, err.message)
+ await logging(
+ "HERE".red + err.code + err.meta + err.message,
+ ["error"],
+ req
+ )
throw sendError(req, res, rejectionErrors.gameNotFound)
}
}
diff --git a/leaky-ships/pages/api/game/running.ts b/leaky-ships/pages/api/game/running.ts
index 9de3c29..8715721 100644
--- a/leaky-ships/pages/api/game/running.ts
+++ b/leaky-ships/pages/api/game/running.ts
@@ -73,7 +73,7 @@ export function composeBody(
return { payload, hash }
}
-export default async function create(
+export default async function running(
req: NextApiRequest,
res: NextApiResponse
) {
diff --git a/leaky-ships/pages/api/ws2.ts b/leaky-ships/pages/api/ws.ts
similarity index 60%
rename from leaky-ships/pages/api/ws2.ts
rename to leaky-ships/pages/api/ws.ts
index a32c5c7..691b342 100644
--- a/leaky-ships/pages/api/ws2.ts
+++ b/leaky-ships/pages/api/ws.ts
@@ -3,6 +3,7 @@ import {
cServer,
} from "../../interfaces/NextApiSocket"
import { composeBody, getAnyRunningGame } from "./game/running"
+import logging from "@lib/backend/logging"
import prisma from "@lib/prisma"
import colors from "colors"
import { NextApiRequest } from "next"
@@ -16,11 +17,11 @@ const SocketHandler = async (
res: NextApiResponseWithSocket
) => {
if (res.socket.server.io) {
- console.log("Socket is already running " + req.url)
+ logging("Socket is already running " + req.url, ["infoCyan"], req)
} else {
- console.log("Socket is initializing " + req.url)
+ logging("Socket is initializing " + req.url, ["infoCyan"], req)
const io: cServer = new Server(res.socket.server, {
- path: "/api/ws2",
+ path: "/api/ws",
cors: {
origin: "https://leaky-ships.mal-noh.de",
},
@@ -41,23 +42,35 @@ const SocketHandler = async (
})
next()
} catch (err) {
- console.log("Unauthorized")
+ logging("Unauthorized", ["warn"], socket.request)
next(new Error("Unauthorized"))
}
})
io.on("connection", (socket) => {
- console.log(
- `User connected <${socket.data.user?.email}>`.green,
- socket.id
+ logging(
+ `User connected <${socket.data.user?.email}>`.green +
+ ", " +
+ socket.id.cyan,
+ ["infoGreen"],
+ socket.request
)
socket.on("join", async (userId, cb) => {
- console.log(socket.rooms, "join")
+ logging(
+ "Join: " + JSON.stringify(Array.from(socket.rooms)),
+ ["debug"],
+ socket.request
+ )
const game = await getAnyRunningGame(userId ?? "")
if (!game) {
- const warst = socket.emit("forbidden")
- console.log("forbidden", warst)
+ socket.emit("forbidden")
+ logging(
+ "Forbidden, no game found: " +
+ JSON.stringify(Array.from(socket.rooms)),
+ ["debug"],
+ socket.request
+ )
return
}
cb()
@@ -68,12 +81,16 @@ const SocketHandler = async (
})
socket.on("disconnecting", () => {
- console.log("disconnecting", socket.rooms) // the Set contains at least the socket ID
+ logging(
+ "Disconnecting: " + JSON.stringify(Array.from(socket.rooms)),
+ ["debug"],
+ socket.request
+ ) // the Set contains at least the socket ID
})
socket.on("disconnect", () => {
// socket.rooms.size === 0
- console.log("disconnect", socket.id)
+ logging("Disconnect: " + socket.id, ["debug"], socket.request)
})
})
}
diff --git a/leaky-ships/pages/game/index.tsx b/leaky-ships/pages/game.tsx
similarity index 55%
rename from leaky-ships/pages/game/index.tsx
rename to leaky-ships/pages/game.tsx
index dcba50e..181b465 100644
--- a/leaky-ships/pages/game/index.tsx
+++ b/leaky-ships/pages/game.tsx
@@ -1,21 +1,15 @@
-import { authOptions } from "../api/auth/[...nextauth]"
-import { getAnyRunningGame } from "../api/game/running"
-import { GetServerSideProps } from "next"
-import { Session, getServerSession } from "next-auth"
+import useGameState from "@lib/hooks/useGameState"
import { useRouter } from "next/router"
import React, { useEffect } from "react"
import { toast } from "react-toastify"
-interface Props {
- gameId: string
- session: Session | null
-}
-
-export default function Game({ gameId, session }: Props) {
+export default function Game() {
+ const { gameProps, session } = useGameState()
const router = useRouter()
useEffect(() => {
- const path = gameId ? "/game/" + gameId : "/start"
+ const gameId = gameProps.payload?.game?.id
+ const path = gameId ? "/game" : "/start"
toast.promise(router.push(path), {
pending: {
render: "Wird weitergeleitet...",
@@ -45,16 +39,3 @@ export default function Game({ gameId, session }: Props) {
)
}
-
-export const getServerSideProps: GetServerSideProps = async (
- context
-) => {
- const session = await getServerSession(context.req, context.res, authOptions)
- let gameId = ""
- if (session?.user.id) {
- const game = await getAnyRunningGame(session?.user.id)
- if (game && game.state === "running") gameId = game?.id
- }
-
- return { props: { gameId, session } }
-}
diff --git a/leaky-ships/pages/lobby/[gameId].tsx b/leaky-ships/pages/lobby.tsx
similarity index 51%
rename from leaky-ships/pages/lobby/[gameId].tsx
rename to leaky-ships/pages/lobby.tsx
index 75f0d9d..eaf01d7 100644
--- a/leaky-ships/pages/lobby/[gameId].tsx
+++ b/leaky-ships/pages/lobby.tsx
@@ -1,17 +1,14 @@
-import BurgerMenu from "../../components/BurgerMenu"
-import LobbyFrame from "../../components/Lobby/LobbyFrame"
-import Settings from "../../components/Lobby/SettingsFrame/Settings"
-import Logo from "../../components/Logo"
-import { composeBody, getAnyGame } from "../api/game/running"
+import BurgerMenu from "../components/BurgerMenu"
+import LobbyFrame from "../components/Lobby/LobbyFrame"
+import Settings from "../components/Lobby/SettingsFrame/Settings"
+import Logo from "../components/Logo"
import useGameState from "@lib/hooks/useGameState"
-import { GamePropsSchema } from "@lib/zodSchemas"
import classNames from "classnames"
-import { GetServerSideProps } from "next"
import Head from "next/head"
import { useState } from "react"
-export default function Home(props: GamePropsSchema) {
- useGameState(props)
+export default function Lobby() {
+ useGameState()
const [settings, setSettings] = useState(false)
return (
@@ -36,22 +33,3 @@ export default function Home(props: GamePropsSchema) {
)
}
-
-export const getServerSideProps: GetServerSideProps = async (
- context
-) => {
- const { gameId } = context.query
-
- const gameIdString = Array.isArray(gameId) ? gameId[0] : gameId
- const game = await getAnyGame(gameIdString ?? "")
- if (!game)
- return {
- redirect: {
- destination: "/start",
- permanent: false,
- },
- }
- const body = composeBody(game)
-
- return { props: body }
-}
diff --git a/leaky-ships/pages/lobby/index.tsx b/leaky-ships/pages/lobby/index.tsx
deleted file mode 100644
index 29fe8b7..0000000
--- a/leaky-ships/pages/lobby/index.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import { authOptions } from "../api/auth/[...nextauth]"
-import { getAnyRunningGame } from "../api/game/running"
-import { GetServerSideProps } from "next"
-import { Session, getServerSession } from "next-auth"
-import { useRouter } from "next/router"
-import React, { useEffect } from "react"
-import { toast } from "react-toastify"
-
-interface Props {
- gameId: string
- session: Session | null
-}
-
-export default function Lobby({ gameId, session }: Props) {
- const router = useRouter()
-
- useEffect(() => {
- const path = gameId ? "/lobby/" + gameId : "/start"
- toast.promise(router.push(path), {
- pending: {
- render: "Wird weitergeleitet...",
- toastId: "redirect",
- },
- success: {
- render: gameId
- ? "Spiel gefunden!"
- : session?.user
- ? "Kein laufendes Spiel."
- : "Kein laufendes Spiel. Bitte anmelden.",
- toastId: session?.user ? "postRedirect" : "user",
- theme: session?.user ? "dark" : undefined,
- type: gameId ? "success" : "info",
- },
- error: {
- render: "Es ist ein Fehler aufgetreten 🤯",
- type: "error",
- theme: "colored",
- },
- })
- })
-
- return (
-
- )
-}
-
-export const getServerSideProps: GetServerSideProps = async (
- context
-) => {
- const session = await getServerSession(context.req, context.res, authOptions)
- let gameId = ""
- if (session?.user.id) {
- const game = await getAnyRunningGame(session?.user.id)
- if (game && game.state === "launching") gameId = game?.id
- }
-
- return { props: { gameId, session } }
-}
diff --git a/leaky-ships/pages/start.tsx b/leaky-ships/pages/start.tsx
index cb2dd02..9b4b3a9 100644
--- a/leaky-ships/pages/start.tsx
+++ b/leaky-ships/pages/start.tsx
@@ -1,25 +1,17 @@
import BurgerMenu from "../components/BurgerMenu"
import Logo from "../components/Logo"
import OptionButton from "../components/OptionButton"
-import { authOptions } from "./api/auth/[...nextauth]"
import { faEye, faLeftLong } from "@fortawesome/pro-regular-svg-icons"
import { faPlus, faUserPlus } from "@fortawesome/pro-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import useGameState from "@lib/hooks/useGameState"
import { GamePropsSchema } from "@lib/zodSchemas"
import status from "http-status"
-import { GetServerSideProps } from "next"
-import { Session, getServerSession } from "next-auth"
import { useRouter } from "next/router"
import { useCallback, useEffect, useMemo, useState } from "react"
import OtpInput from "react-otp-input"
import { toast } from "react-toastify"
-interface Props {
- q: string | string[] | undefined
- session: Session | null
-}
-
function isInputOnlyNumbers(input: string) {
return /^\d+$/.test(input)
}
@@ -51,16 +43,21 @@ const handleConfirmation = () => {
)
}
-export default function Start({ q, session: initSession }: Props) {
+export default function Start() {
const [otp, setOtp] = useState("")
- const { gameProps, setGameProps } = useGameState()
+ const { session, setGameProps } = useGameState()
const router = useRouter()
- const { session: sessionUsed } = useGameState()
- const session = useMemo(
- () => (sessionUsed ? sessionUsed : initSession),
- [sessionUsed, initSession]
- )
+ const query = useMemo((): { join?: boolean; watch?: boolean } => {
+ switch (router.query.q) {
+ case "join":
+ return { join: true }
+ case "watch":
+ return { watch: true }
+ default:
+ return {}
+ }
+ }, [router])
const gameFetch = useCallback(
async (pin?: string) => {
@@ -71,25 +68,29 @@ export default function Start({ q, session: initSession }: Props) {
.then(isAuthenticated)
.then((game) => GamePropsSchema.parse(game))
+ const toastId = !pin ? "erstellt" : "angefragt"
const res = await toast.promise(gamePromise, {
pending: {
- render: "Raum wird " + (!pin ? "erstellt" : "angefragt"),
+ render: "Raum wird " + toastId,
+ toastId,
},
success: {
render: "Raum " + (!pin ? "erstellt" : "angefragt") + " 👌",
type: "info",
theme: "colored",
+ toastId,
},
error: {
render: "Es ist ein Fehler aufgetreten 🤯",
type: "error",
theme: "colored",
+ toastId,
},
})
setGameProps(res)
- await toast.promise(router.push("/lobby/" + res.payload.game?.id), {
+ await toast.promise(router.push("/lobby"), {
pending: {
render: "Raum wird beigetreten",
},
@@ -150,7 +151,7 @@ export default function Start({ q, session: initSession }: Props) {
icon={faUserPlus}
disabled={!session}
>
- {q === "join" && session?.user ? (
+ {query.join && session?.user ? (
- {q === "watch" && session?.user ? (
+ {query.watch && session?.user ? (
)
}
-
-export const getServerSideProps: GetServerSideProps = async (
- context
-) => {
- const session = await getServerSession(context.req, context.res, authOptions)
- const { q } = context.query
-
- return { props: { q, session } }
-}