From 6ce09a61b4573fc2137b76fd8d972baf531d7246 Mon Sep 17 00:00:00 2001 From: Takumi Hara <69781798+takumihara@users.noreply.github.com> Date: Mon, 4 Mar 2024 00:21:57 -0600 Subject: [PATCH] Refac pong board component (#291) * Remnove `useStateCallback * Remove unncesary info * Remove game.setUserMode * UserModeType -> UserMode * Create useGame hook * Move userMode to useGame * Move player info to useGamte * Create usePlayers * Remove memoization * Move theme change logic to useGame * Create useGameSocket * Move useGameSocket to useGame * Move game ticking logic to useGame * Create useGameKeyboard * create useGameTheme * Create useGetGame * Define DEFAULT_COLOR in const * Create lib/hooks/game/ folder * Format --- backend/src/events/events.gateway.ts | 1 + frontend/app/lib/hooks/game/useGame.ts | 57 +++ .../app/lib/hooks/game/useGameKeyboard.ts | 28 ++ frontend/app/lib/hooks/game/useGameSocket.ts | 244 ++++++++++++ frontend/app/lib/hooks/game/useGameTheme.ts | 17 + frontend/app/lib/hooks/game/useGetGame.ts | 26 ++ frontend/app/lib/hooks/usePlayers.ts | 32 ++ frontend/app/lib/hooks/useUserMode.ts | 6 +- frontend/app/pong/[id]/PongBoard.tsx | 357 +----------------- frontend/app/pong/[id]/PongGame.ts | 29 -- .../app/pong/[id]/PongInformationBoard.tsx | 16 - frontend/app/pong/[id]/const.ts | 2 + 12 files changed, 422 insertions(+), 393 deletions(-) create mode 100644 frontend/app/lib/hooks/game/useGame.ts create mode 100644 frontend/app/lib/hooks/game/useGameKeyboard.ts create mode 100644 frontend/app/lib/hooks/game/useGameSocket.ts create mode 100644 frontend/app/lib/hooks/game/useGameTheme.ts create mode 100644 frontend/app/lib/hooks/game/useGetGame.ts create mode 100644 frontend/app/lib/hooks/usePlayers.ts diff --git a/backend/src/events/events.gateway.ts b/backend/src/events/events.gateway.ts index 3c12f521..db22b03b 100644 --- a/backend/src/events/events.gateway.ts +++ b/backend/src/events/events.gateway.ts @@ -143,6 +143,7 @@ export class EventsGateway implements OnGatewayDisconnect { if (opponent) { this.emitUpdateStatus(client, 'ready', { user: this.users[opponent], + playerNumber: this.players[gameId][opponent], }); } this.lostPoints[client.id] = 0; diff --git a/frontend/app/lib/hooks/game/useGame.ts b/frontend/app/lib/hooks/game/useGame.ts new file mode 100644 index 00000000..306210f9 --- /dev/null +++ b/frontend/app/lib/hooks/game/useGame.ts @@ -0,0 +1,57 @@ +import { useEffect, useRef, useState } from "react"; +import { useUserMode } from "../useUserMode"; +import { UserEntity } from "../../dtos"; +import usePlayers from "../usePlayers"; +import useGameSocket from "./useGameSocket"; +import { TARGET_FRAME_MS } from "@/app/pong/[id]/const"; +import useGameKeyboard from "./useGameKeyboard"; +import useGameTheme from "./useGameTheme"; +import useGetGame from "./useGetGame"; + +export default function useGame( + id: string, + currentUser?: UserEntity, + resolvedTheme?: string, +) { + const canvasRef = useRef(null); // only initialized once + const [userMode, setUserMode] = useUserMode(); + const { getGame } = useGetGame(canvasRef, userMode); + const [logs, setLogs] = useState([]); + const [startDisabled, setStartDisabled] = useState(true); + + const { leftPlayer, rightPlayer, getPlayerSetterFromPlayerNumber } = + usePlayers(userMode, currentUser); + + useGameTheme(getGame, resolvedTheme); + useGameKeyboard(getGame); + + const { start } = useGameSocket( + id, + getGame, + setLogs, + userMode, + setUserMode, + getPlayerSetterFromPlayerNumber, + setStartDisabled, + currentUser, + ); + + useEffect(() => { + const game = getGame(); + game.draw_canvas(); + const intervalId = setInterval(game.update, TARGET_FRAME_MS); + + return () => clearInterval(intervalId); + }, [getGame]); + + return { + getGame, + canvasRef, + userMode, + leftPlayer, + rightPlayer, + logs, + start, + startDisabled, + }; +} diff --git a/frontend/app/lib/hooks/game/useGameKeyboard.ts b/frontend/app/lib/hooks/game/useGameKeyboard.ts new file mode 100644 index 00000000..12908809 --- /dev/null +++ b/frontend/app/lib/hooks/game/useGameKeyboard.ts @@ -0,0 +1,28 @@ +import { PongGame } from "@/app/pong/[id]/PongGame"; +import { useEffect } from "react"; + +export default function useGameKeyboard(getGame: () => PongGame) { + useEffect(() => { + const game = getGame(); + + const handleKeyUp = (event: KeyboardEvent) => { + if (event.key == "ArrowDown" || event.key == "ArrowUp") { + game.setMovingDirection("none"); + } + }; + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key == "ArrowDown") { + game.setMovingDirection("right"); + } else if (event.key == "ArrowUp") { + game.setMovingDirection("left"); + } + }; + + document.addEventListener("keydown", handleKeyDown); + document.addEventListener("keyup", handleKeyUp); + return () => { + document.removeEventListener("keydown", handleKeyDown); + document.removeEventListener("keyup", handleKeyUp); + }; + }, [getGame]); +} diff --git a/frontend/app/lib/hooks/game/useGameSocket.ts b/frontend/app/lib/hooks/game/useGameSocket.ts new file mode 100644 index 00000000..d822002d --- /dev/null +++ b/frontend/app/lib/hooks/game/useGameSocket.ts @@ -0,0 +1,244 @@ +import { PongGame } from "@/app/pong/[id]/PongGame"; +import { useCallback, useEffect, useRef } from "react"; +import { Socket, io } from "socket.io-client"; +import { UserMode } from "../useUserMode"; +import { UserEntity } from "../../dtos"; +import { POINT_TO_WIN } from "@/app/pong/[id]/const"; + +type Status = + | "too-many-players" + | "joined-as-player" + | "joined-as-viewer" + | "ready" + | "login-required" + | "friend-joined" + | "friend-left" + | "won" + | "lost" + | "finish"; + +interface HandleActionProps { + playerNumber: number; +} + +const getLogFromStatus = (status: Status): string => { + switch (status) { + case "too-many-players": + return "There are too many players. You can only watch the game"; + case "joined-as-player": + return "You have joined as player"; + case "joined-as-viewer": + return "You have joined as viewer"; + case "ready": + return "Your friend is already here. The game is ready to start"; + case "login-required": + return "You need to login to play."; + case "friend-joined": + return "Your friend has joined the game"; + case "friend-left": + return "Your friend has left"; + case "won": + return "You won!"; + case "lost": + return "You lost!"; + case "finish": + return "The game has finished"; + } +}; + +export default function useGameSocket( + id: string, + getGame: () => PongGame, + setLogs: (fun: (logs: string[]) => string[]) => void, + userMode: UserMode, + setUserMode: (mode: UserMode) => void, + getPlayerSetterFromPlayerNumber: ( + playerNumber: number, + ) => (user: any) => void, + setStartDisabled: (disabled: boolean) => void, + currentUser?: UserEntity, +) { + const socketRef = useRef(null); // updated on `id` change + + const start = useCallback(() => { + if (!userMode) return; + const game = getGame(); + + setStartDisabled(true); + + const { vx, vy } = game.start({ vx: undefined, vy: undefined }); + socketRef.current?.emit("start", { + vx: -vx, + vy: -vy, + }); + }, [getGame, userMode, setStartDisabled]); + + const runSideEffectForStatusUpdate = useCallback( + (status: Status, payload: any) => { + const game = getGame(); + + switch (status) { + case "too-many-players": + // TODO: users cannot really see the log + setUserMode("viewer"); + break; + case "login-required": + // TODO: instead of redirect. Show modal to login + setUserMode("viewer"); + break; + case "friend-joined": + const { playerNumber, user } = payload; + const setter = getPlayerSetterFromPlayerNumber(playerNumber); + setter(user); + currentUser && setStartDisabled(false); + game.resetPlayerPosition(); + break; + case "friend-left": + { + const { playerNumber } = payload; + const setter = getPlayerSetterFromPlayerNumber(playerNumber); + setter(undefined); + setStartDisabled(true); + } + break; + case "joined-as-viewer": + { + const { players } = payload; + players.forEach(({ playerNumber, user }: any) => { + const setter = getPlayerSetterFromPlayerNumber(playerNumber); + setter(user); + }); + } + break; + case "ready": { + { + const { user, playerNumber } = payload; + const setter = getPlayerSetterFromPlayerNumber(playerNumber); + setter(user); + } + break; + } + } + }, + [ + currentUser, + setUserMode, + getGame, + getPlayerSetterFromPlayerNumber, + setStartDisabled, + ], + ); + + useEffect(() => { + const socket = io("/pong", { + query: { game_id: id, is_player: userMode === "player" }, + forceNew: true, + }); + socketRef.current = socket; + + const game = getGame(); + game.onAction = (action: string) => { + socket.emit(action); + }; + + const handleUpdateStatus = ({ + status, + payload, + }: { + status: Status; + payload: any; + }) => { + runSideEffectForStatusUpdate(status, payload); + const log = getLogFromStatus(status); + setLogs((logs) => [...logs, log]); + }; + const handleConnect = () => { + console.log(`Connected: ${socketRef.current?.id}`); + const log = "Connected to server"; + setLogs((logs) => [...logs, log]); + }; + + const handleStart = (data: { vx: number; vy: number }) => { + game.start(data); + setStartDisabled(true); + }; + + const handleRight = ({ playerNumber }: HandleActionProps) => { + if (userMode !== "player" && playerNumber == 1) { + game.movePlayer1Left(); + } else { + game.movePlayer2Left(); + } + }; + + const handleLeft = ({ playerNumber }: HandleActionProps) => { + if (userMode !== "player" && playerNumber == 1) { + game.movePlayer1Right(); + } else { + game.movePlayer2Right(); + } + }; + + const handleBounce = ({ playerNumber }: HandleActionProps) => { + if (userMode !== "player" && playerNumber == 1) { + game.bounceOffPaddlePlayer1(); + } else { + game.bounceOffPaddlePlayer2(); + } + }; + + const handleCollide = (msg: HandleActionProps) => { + const { playerNumber } = msg; + if (userMode === "player") { + const score = game.increaseScorePlayer1(); + if (score != POINT_TO_WIN) { + setTimeout(() => start(), 1000); + } + } else { + if (playerNumber == 1) { + game.increaseScorePlayer2(); + } else { + game.increaseScorePlayer1(); + } + } + game.endRound(); + }; + + const handleFinish = () => { + const game = getGame(); + game.stop(); + }; + + socket.on("connect", handleConnect); + socket.on("start", handleStart); + socket.on("right", handleRight); + socket.on("left", handleLeft); + socket.on("bounce", handleBounce); + socket.on("collide", handleCollide); + socket.on("update-status", handleUpdateStatus); + socket.on("finish", handleFinish); + + return () => { + socket.off("connect", handleConnect); + socket.off("start", handleStart); + socket.off("right", handleRight); + socket.off("left", handleLeft); + socket.off("bounce", handleBounce); + socket.off("collide", handleCollide); + socket.off("update-status", handleUpdateStatus); + socket.off("finish", handleFinish); + socket.disconnect(); + }; + }, [ + id, + getGame, + setLogs, + start, + userMode, + setUserMode, + runSideEffectForStatusUpdate, + setStartDisabled, + ]); + + return { socketRef, start }; +} diff --git a/frontend/app/lib/hooks/game/useGameTheme.ts b/frontend/app/lib/hooks/game/useGameTheme.ts new file mode 100644 index 00000000..52772936 --- /dev/null +++ b/frontend/app/lib/hooks/game/useGameTheme.ts @@ -0,0 +1,17 @@ +import { PongGame } from "@/app/pong/[id]/PongGame"; +import { useEffect } from "react"; + +export default function useGameTheme( + getGame: () => PongGame, + resolvedTheme?: string, +) { + useEffect(() => { + // TODO: Use --foreground color from CSS + // Somehow it didn't work (theme is changed but not yet committed to CSS/DOM?) + const game = getGame(); + const color = + resolvedTheme === "dark" ? "hsl(0, 0%, 100%)" : "hsl(0, 0%, 0%)"; + game.setColor(color); + game.draw_canvas(); + }, [resolvedTheme, getGame]); +} diff --git a/frontend/app/lib/hooks/game/useGetGame.ts b/frontend/app/lib/hooks/game/useGetGame.ts new file mode 100644 index 00000000..b292d053 --- /dev/null +++ b/frontend/app/lib/hooks/game/useGetGame.ts @@ -0,0 +1,26 @@ +import { PongGame } from "@/app/pong/[id]/PongGame"; +import { MutableRefObject, useCallback, useRef } from "react"; +import { UserMode } from "../useUserMode"; +import { DEFAULT_COLOR } from "@/app/pong/[id]/const"; + +export default function useGetGame( + canvasRef: MutableRefObject, + userMode: UserMode, +) { + const gameRef = useRef(null); // only initialized once + + const getGame = useCallback(() => { + const ctx = canvasRef.current?.getContext("2d"); + if (!ctx) { + throw new Error("canvas not ready"); + } + if (!gameRef.current) { + const game = new PongGame(ctx, DEFAULT_COLOR, DEFAULT_COLOR, userMode); + gameRef.current = game; + return game; + } + return gameRef.current; + }, [canvasRef, userMode]); + + return { getGame }; +} diff --git a/frontend/app/lib/hooks/usePlayers.ts b/frontend/app/lib/hooks/usePlayers.ts new file mode 100644 index 00000000..1255725c --- /dev/null +++ b/frontend/app/lib/hooks/usePlayers.ts @@ -0,0 +1,32 @@ +import { useCallback, useState } from "react"; +import { UserMode } from "./useUserMode"; +import { PublicUserEntity, UserEntity } from "../dtos"; + +export default function usePlayers( + userMode: UserMode, + currentUser?: UserEntity, +) { + const [leftPlayer, setLeftPlayer] = useState( + () => (userMode === "player" ? currentUser : undefined), + ); + const [rightPlayer, setRightPlayer] = useState( + undefined, + ); + + const getPlayerSetterFromPlayerNumber = useCallback( + (playerNumber: number) => { + return userMode == "player" + ? setRightPlayer + : playerNumber == 1 + ? setLeftPlayer + : setRightPlayer; + }, + [userMode], + ); + + return { + leftPlayer, + rightPlayer, + getPlayerSetterFromPlayerNumber, + }; +} diff --git a/frontend/app/lib/hooks/useUserMode.ts b/frontend/app/lib/hooks/useUserMode.ts index 8fcb3aaf..239dc04c 100644 --- a/frontend/app/lib/hooks/useUserMode.ts +++ b/frontend/app/lib/hooks/useUserMode.ts @@ -1,16 +1,16 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { useCallback } from "react"; -export type UserModeType = "viewer" | "player"; +export type UserMode = "viewer" | "player"; -export function useUserMode(): [UserModeType, (mode: UserModeType) => void] { +export function useUserMode(): [UserMode, (mode: UserMode) => void] { const pathname = usePathname(); const { replace } = useRouter(); const searchParams = useSearchParams(); const userMode = searchParams.get("mode") == "player" ? "player" : "viewer"; const setUserMode = useCallback( - (mode: UserModeType) => { + (mode: UserMode) => { const params = new URLSearchParams(searchParams); params.set("mode", mode); replace(`${pathname}?${params.toString()}`); diff --git a/frontend/app/pong/[id]/PongBoard.tsx b/frontend/app/pong/[id]/PongBoard.tsx index 91d1e0e6..f1a43c50 100644 --- a/frontend/app/pong/[id]/PongBoard.tsx +++ b/frontend/app/pong/[id]/PongBoard.tsx @@ -1,353 +1,28 @@ "use client"; import { useAuthContext } from "@/app/lib/client-auth"; -import { PublicUserEntity } from "@/app/lib/dtos"; -import { useUserMode } from "@/app/lib/hooks/useUserMode"; import { Button } from "@/components/ui/button"; import { useTheme } from "next-themes"; -import { memo, useCallback, useEffect, useRef, useState } from "react"; -import type { Socket } from "socket.io-client"; -import { io } from "socket.io-client"; -import { PongGame } from "./PongGame"; import PongInformationBoard from "./PongInformationBoard"; -import { CANVAS_HEIGHT, CANVAS_WIDTH, TARGET_FRAME_MS } from "./const"; - -type Status = - | "too-many-players" - | "joined-as-player" - | "joined-as-viewer" - | "ready" - | "login-required" - | "friend-joined" - | "friend-left" - | "won" - | "lost" - | "finish"; - -type setState = T | ((prevState: T) => T); - -function useStateCallback( - initialState: T | (() => T), -): [T, (arg: setState) => void] { - const [state, setState] = useState(initialState); - const memoizedSetFunction = useCallback( - (arg: setState) => setState(arg), - [], - ); - - return [state, memoizedSetFunction]; -} +import { CANVAS_HEIGHT, CANVAS_WIDTH } from "./const"; +import useGame from "@/app/lib/hooks/game/useGame"; interface PongBoardProps { id: string; } -interface HandleActionProps { - playerNumber: number; -} - -const POINT_TO_WIN = 3; - -const getLogFromStatus = (status: Status) => { - switch (status) { - case "too-many-players": - return "There are too many players. You can only watch the game"; - case "joined-as-player": - return "You have joined as player"; - case "joined-as-viewer": - return "You have joined as viewer"; - case "ready": - return "Your friend is already here. The game is ready to start"; - case "login-required": - return "You need to login to play."; - case "friend-joined": - return "Your friend has joined the game"; - case "friend-left": - return "Your friend has left"; - case "won": - return "You won!"; - case "lost": - return "You lost!"; - case "finish": - return "The game has finished"; - } -}; - -function PongBoard({ id }: PongBoardProps) { - const [fps, setFps] = useStateCallback(0); - const [speed, setSpeed] = useStateCallback(0); - const [player1Position, setPlayer1Position] = useStateCallback(0); - const [player2Position, setPlayer2Position] = useStateCallback(0); - const [logs, setLogs] = useStateCallback([]); - const [userMode, setUserMode] = useUserMode(); - - const canvasRef = useRef(null); // only initialized once - const gameRef = useRef(null); // only initialized once - const socketRef = useRef(null); // updated on `id` change - const [startDisabled, setStartDisabled] = useState(true); - const { resolvedTheme } = useTheme(); - const defaultColor = "hsl(0, 0%, 0%)"; - +export default function PongBoard({ id }: PongBoardProps) { const { currentUser } = useAuthContext(); - - const [leftPlayer, setLeftPlayer] = useState( - () => (userMode === "player" ? currentUser : undefined), - ); - const [rightPlayer, setRightPlayer] = useState( - undefined, - ); - - const getPlayerSetterFromPlayerNumber = useCallback( - (playerNumber: number) => { - return userMode == "player" - ? setRightPlayer - : playerNumber == 1 - ? setLeftPlayer - : setRightPlayer; - }, - [userMode], - ); - - const getGame = useCallback(() => { - const ctx = canvasRef.current?.getContext("2d"); - if (!ctx) { - throw new Error("canvas not ready"); - } - if (!gameRef.current) { - const game = new PongGame( - ctx, - setFps, - setSpeed, - setPlayer1Position, - setPlayer2Position, - defaultColor, - defaultColor, - userMode, - ); - gameRef.current = game; - return game; - } - return gameRef.current; - }, [setFps, setSpeed, setPlayer1Position, setPlayer2Position, userMode]); - - const start = useCallback(() => { - if (!userMode) return; - const game = getGame(); - - setStartDisabled(true); - - const { vx, vy } = game.start({ vx: undefined, vy: undefined }); - socketRef.current?.emit("start", { - vx: -vx, - vy: -vy, - }); - }, [getGame, userMode]); - - const runSideEffectForStatusUpdate = useCallback( - (status: Status, payload: any) => { - const game = getGame(); - - switch (status) { - case "too-many-players": - // TODO: users cannot really see the log - setUserMode("viewer"); - break; - case "login-required": - // TODO: instead of redirect. Show modal to login - setUserMode("viewer"); - break; - case "friend-joined": - const { playerNumber, user } = payload; - const setter = getPlayerSetterFromPlayerNumber(playerNumber); - setter(user); - currentUser && setStartDisabled(false); - game.resetPlayerPosition(); - break; - case "friend-left": - { - const { playerNumber } = payload; - const setter = getPlayerSetterFromPlayerNumber(playerNumber); - setter(undefined); - setStartDisabled(true); - } - break; - case "joined-as-viewer": - { - const { players } = payload; - players.forEach(({ playerNumber, user }: any) => { - const setter = getPlayerSetterFromPlayerNumber(playerNumber); - setter(user); - }); - } - break; - case "ready": { - { - const { user } = payload; - setRightPlayer(user); - } - break; - } - } - }, - [currentUser, setUserMode, getGame, getPlayerSetterFromPlayerNumber], - ); - - useEffect(() => { - const game = getGame(); - game.setUserMode(userMode); - }, [getGame, userMode]); - - useEffect(() => { - // TODO: Use --foreground color from CSS - // Somehow it didn't work (theme is changed but not yet committed to CSS/DOM?) - const game = getGame(); - const color = - resolvedTheme === "dark" ? "hsl(0, 0%, 100%)" : "hsl(0, 0%, 0%)"; - game.setColor(color); - game.draw_canvas(); - }, [resolvedTheme, getGame]); - - useEffect(() => { - const game = getGame(); - game.draw_canvas(); - const intervalId = setInterval(game.update, TARGET_FRAME_MS); - - return () => clearInterval(intervalId); - }, [getGame]); - - useEffect(() => { - const game = getGame(); - - const handleKeyUp = (event: KeyboardEvent) => { - if (event.key == "ArrowDown" || event.key == "ArrowUp") { - game.setMovingDirection("none"); - } - }; - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key == "ArrowDown") { - game.setMovingDirection("right"); - } else if (event.key == "ArrowUp") { - game.setMovingDirection("left"); - } - }; - - document.addEventListener("keydown", handleKeyDown); - document.addEventListener("keyup", handleKeyUp); - return () => { - document.removeEventListener("keydown", handleKeyDown); - document.removeEventListener("keyup", handleKeyUp); - }; - }, [getGame]); - - useEffect(() => { - const socket = io("/pong", { - query: { game_id: id, is_player: userMode === "player" }, - forceNew: true, - }); - socketRef.current = socket; - - const game = getGame(); - game.onAction = (action: string) => { - socket.emit(action); - }; - - const handleUpdateStatus = ({ - status, - payload, - }: { - status: Status; - payload: any; - }) => { - runSideEffectForStatusUpdate(status, payload); - const log = getLogFromStatus(status); - setLogs((logs) => [...logs, log]); - }; - const handleConnect = () => { - console.log(`Connected: ${socketRef.current?.id}`); - const log = "Connected to server"; - setLogs((logs) => [...logs, log]); - }; - - const handleStart = (data: { vx: number; vy: number }) => { - game.start(data); - setStartDisabled(true); - }; - - const handleRight = ({ playerNumber }: HandleActionProps) => { - if (userMode !== "player" && playerNumber == 1) { - game.movePlayer1Left(); - } else { - game.movePlayer2Left(); - } - }; - - const handleLeft = ({ playerNumber }: HandleActionProps) => { - if (userMode !== "player" && playerNumber == 1) { - game.movePlayer1Right(); - } else { - game.movePlayer2Right(); - } - }; - - const handleBounce = ({ playerNumber }: HandleActionProps) => { - if (userMode !== "player" && playerNumber == 1) { - game.bounceOffPaddlePlayer1(); - } else { - game.bounceOffPaddlePlayer2(); - } - }; - - const handleCollide = (msg: HandleActionProps) => { - const { playerNumber } = msg; - if (userMode === "player") { - const score = game.increaseScorePlayer1(); - if (score != POINT_TO_WIN) { - setTimeout(() => start(), 1000); - } - } else { - if (playerNumber == 1) { - game.increaseScorePlayer2(); - } else { - game.increaseScorePlayer1(); - } - } - game.endRound(); - }; - - const handleFinish = () => { - const game = getGame(); - game.stop(); - }; - - socket.on("connect", handleConnect); - socket.on("start", handleStart); - socket.on("right", handleRight); - socket.on("left", handleLeft); - socket.on("bounce", handleBounce); - socket.on("collide", handleCollide); - socket.on("update-status", handleUpdateStatus); - socket.on("finish", handleFinish); - - return () => { - socket.off("connect", handleConnect); - socket.off("start", handleStart); - socket.off("right", handleRight); - socket.off("left", handleLeft); - socket.off("bounce", handleBounce); - socket.off("collide", handleCollide); - socket.off("update-status", handleUpdateStatus); - socket.off("finish", handleFinish); - socket.disconnect(); - }; - }, [ - id, - getGame, - setLogs, - start, + const { resolvedTheme } = useTheme(); + const { + canvasRef, userMode, - setUserMode, - runSideEffectForStatusUpdate, - ]); + leftPlayer, + rightPlayer, + logs, + start, + startDisabled, + } = useGame(id, currentUser, resolvedTheme); return (
@@ -364,10 +39,6 @@ function PongBoard({ id }: PongBoardProps) {
); } - -const memoizedPongBoard = memo(PongBoard); -export default memoizedPongBoard; -// export default PongBoard; diff --git a/frontend/app/pong/[id]/PongGame.ts b/frontend/app/pong/[id]/PongGame.ts index 657e1586..62f20b87 100644 --- a/frontend/app/pong/[id]/PongGame.ts +++ b/frontend/app/pong/[id]/PongGame.ts @@ -28,19 +28,11 @@ export class PongGame { private is_playing: boolean; private movingDirection: movingDirectionType = "none"; - setFps: setFunction; - setSpeed: setFunction; - setPlayer1Position: setFunction; - setPlayer2Position: setFunction; onAction: onActionType | undefined; private userMode: userModeType; constructor( ctx: CanvasRenderingContext2D, - setFps: setFunction, - setSpeed: setFunction, - setPlayer1Position: setFunction, - setPlayer2Position: setFunction, paddleColor: string, ballColor: string, userMode: userModeType, @@ -71,10 +63,6 @@ export class PongGame { this.elapsed = 0; this.frame_count = 0; this.is_playing = false; - this.setFps = setFps; - this.setSpeed = setSpeed; - this.setPlayer1Position = setPlayer1Position; - this.setPlayer2Position = setPlayer2Position; this.userMode = userMode; } @@ -88,21 +76,11 @@ export class PongGame { const fps = Math.round( this.frame_count / (elapsed_since_last_update / 1000), ); - this.setFps(fps); this.frame_count = 0; this.fps_updated_at = this.updated_at; } }; - update_speed(speed: number) { - this.setSpeed(speed); - } - - update_players() { - this.setPlayer1Position(this.player1.x); - this.setPlayer2Position(this.player2.x); - } - draw_canvas = () => { // Clear objects this.ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); @@ -142,9 +120,6 @@ export class PongGame { const now = Date.now(); this.elapsed = this.updated_at === undefined ? 0 : now - this.updated_at; this.updated_at = now; - this.update_fps(); - this.update_speed(this.ball.speed()); - this.update_players(); if (this.userMode === "player") { if (this.movingDirection === "left") { this.player1.clear(this.ctx); @@ -271,8 +246,4 @@ export class PongGame { this.player1.color = color; this.player2.color = color; } - - setUserMode(userMode: userModeType) { - this.userMode = userMode; - } } diff --git a/frontend/app/pong/[id]/PongInformationBoard.tsx b/frontend/app/pong/[id]/PongInformationBoard.tsx index 07da83d0..ffb86598 100644 --- a/frontend/app/pong/[id]/PongInformationBoard.tsx +++ b/frontend/app/pong/[id]/PongInformationBoard.tsx @@ -4,10 +4,6 @@ import { PublicUserEntity } from "@/app/lib/dtos"; import { GameCard } from "@/app/ui/pong/GameCard"; interface PongInformationBoardProps { - fps: number; - speed: number; - player1Position: number; - player2Position: number; logs: string[]; userMode: "viewer" | "player"; leftPlayer?: PublicUserEntity; @@ -15,10 +11,6 @@ interface PongInformationBoardProps { } export default function PongInformationBoard({ - fps, - speed, - player1Position, - player2Position, logs, userMode, leftPlayer, @@ -28,14 +20,6 @@ export default function PongInformationBoard({
You are a {userMode}
-
FPS: {fps}
-
Speed: {speed}
-
- player1: {player1Position} -
-
- player2: {player2Position} -