+
+ {/* Empty state */}
+ {players.length === 0 && (
+
+ )}
+
+ {/* Players list */}
{sortedPlayers.map((player, index) => (
+ {/* Online indicator */}
+ {player.isOnline && (
+
+ )}
+
{player.emoji}
-
+
{player.name}
+
+ {/* Ready indicator */}
+ {player.isReady && (
+
+ â KĂ©sz
+
+ )}
+
+ {/* Current turn indicator */}
+ {currentTurn === player.id && (
+
+ ⶠKöre
+
+ )}
+
+ {/* Rank medal */}
@@ -225,31 +386,33 @@ const GameScreen = () => {
DobĂłkocka
- Kattints a kockĂĄra dobĂĄshoz vagy vĂĄlassz egy szĂĄmot az alĂĄbbibĂłl!
+ Kattints a kockĂĄra dobĂĄshoz!
- {/* Dropdown to select number 1-6 (triggers animated roll to that number) */}
-
-
-
-
-
+
+
+ {/* Connection warning */}
+ {!isConnected && (
+
+ â ïž Nincs kapcsolat a szerverrel
+
+ )}
+
+ {/* Debug Info Panel (Development only) */}
+ {import.meta.env.DEV && (
+
+
đ§ Debug Info
+
+
đĄ Connected: {isConnected ? 'â
' : 'â'}
+
đź Game Code: {gameState?.gameCode || 'N/A'}
+
đ„ Players: {backendPlayers?.length || 0}
+
đČ Board Fields: {boardData?.fields?.length || 0}
+
đ Current Turn: {currentTurn || 'N/A'}
+
đ Token: {gameToken ? 'â
' : 'â'}
+
+
+ )}
diff --git a/SerpentRace_Frontend/src/pages/Game/GameTest.jsx b/SerpentRace_Frontend/src/pages/Game/GameTest.jsx
new file mode 100644
index 00000000..7a743e28
--- /dev/null
+++ b/SerpentRace_Frontend/src/pages/Game/GameTest.jsx
@@ -0,0 +1,160 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { createGame, joinGame } from '../../api/gameApi';
+
+const GameTest = () => {
+ const navigate = useNavigate();
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [gameCode, setGameCode] = useState('');
+ const [createdGameCode, setCreatedGameCode] = useState('');
+ const [showSuccess, setShowSuccess] = useState(false);
+
+ const handleCreateGame = async () => {
+ setLoading(true);
+ setError(null);
+ setShowSuccess(false);
+ try {
+ const token = localStorage.getItem('token');
+ if (!token) {
+ setError('Please login first at /login');
+ return;
+ }
+
+ const gameData = {
+ deckids: ['99333c9a-5928-4788-b852-fa482d34ce56'], // Test deck ID as array
+ maxplayers: 4,
+ logintype: 0, // 0=PUBLIC
+ };
+
+ const response = await createGame(gameData);
+ console.log('Game created:', response);
+
+ // Backend returns game object directly
+ const code = response.gamecode || response.gameCode;
+ if (code) {
+ setCreatedGameCode(code);
+ setShowSuccess(true);
+ }
+
+ // Store game token if provided
+ if (response.gameToken) {
+ localStorage.setItem('gameToken', response.gameToken);
+ }
+
+ // Wait 3 seconds to show code, then navigate
+ setTimeout(() => {
+ navigate('/lobby', { state: { gameCode: code } });
+ }, 3000);
+ } catch (err) {
+ setError(err.response?.data?.message || 'Failed to create game');
+ console.error('Create game error:', err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleJoinGame = async () => {
+ setLoading(true);
+ setError(null);
+ try {
+ const token = localStorage.getItem('token');
+ if (!token) {
+ setError('KĂ©rlek jelentkezz be elĆször a /login oldalon');
+ return;
+ }
+
+ const joinData = {
+ gameCode: gameCode.toUpperCase(),
+ playerName: localStorage.getItem('username') || 'Test Player',
+ };
+
+ const response = await joinGame(joinData);
+ console.log('Joined game:', response);
+
+ // Store game token
+ if (response.data?.gameToken) {
+ localStorage.setItem('gameToken', response.data.gameToken);
+ navigate('/lobby', { state: { gameCode: gameCode.toUpperCase() } });
+ }
+ } catch (err) {
+ setError(err.response?.data?.message || 'Nem sikerĂŒlt csatlakozni a jĂĄtĂ©khoz');
+ console.error('Join game error:', err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
+
Game Test
+
+ {error && (
+
+ {error}
+
+ )}
+
+ {showSuccess && createdGameCode && (
+
+
Game Created!
+
+ {createdGameCode}
+
+
+ Share this code with other players so they can join!
+
+
+ Redirecting to game in 3 seconds...
+
+
+ )}
+
+
+
+
+
OR
+
+
+ setGameCode(e.target.value)}
+ placeholder="Enter Game Code"
+ className="w-full bg-gray-700 text-white px-4 py-2 rounded mb-2"
+ />
+
+
+
+
+
+
Quick Access (Dev Only):
+
+
+
+
+ );
+};
+
+export default GameTest;
diff --git a/SerpentRace_Frontend/src/pages/Game/Lobby.jsx b/SerpentRace_Frontend/src/pages/Game/Lobby.jsx
index a1d6368c..fdc8df58 100644
--- a/SerpentRace_Frontend/src/pages/Game/Lobby.jsx
+++ b/SerpentRace_Frontend/src/pages/Game/Lobby.jsx
@@ -3,6 +3,8 @@ import { useNavigate, useLocation } from "react-router-dom"
import Navbar from "../../components/Navbar/Navbar.jsx"
import Background from "../../assets/backgrounds/Background.jsx"
import useRequireAuth from "../../hooks/useRequireAuth.jsx"
+import { useGameWebSocket } from "../../hooks/useGameWebSocket.js"
+import { startGame } from "../../api/gameApi.js"
const Lobby = () => {
const [visible, setVisible] = useState(false)
@@ -11,6 +13,30 @@ const Lobby = () => {
const location = useLocation()
const [user, setUser] = useRequireAuth()
+
+ // Get game code from location state or WebSocket
+ const gameCodeFromState = location.state?.gameCode
+ const gameToken = localStorage.getItem('gameToken')
+
+ const {
+ isConnected,
+ gameState,
+ players,
+ isGamemaster,
+ gameStarted,
+ } = useGameWebSocket(gameToken)
+
+ const gameCode = gameCodeFromState || gameState?.gameCode || 'Loading...'
+
+ // Filter out gamemaster from player list - gamemaster is NOT a player
+ const currentPlayers = (players || []).filter(p => {
+ // If we have userId info, filter by that
+ if (p.userId) {
+ return p.userId !== gameState?.createdBy
+ }
+ // Otherwise filter by name (less reliable but works for now)
+ return true
+ })
useEffect(() => {
const observer = new IntersectionObserver(
@@ -23,12 +49,48 @@ const Lobby = () => {
return () => observer.disconnect()
}, [])
+ // Auto-navigate when game starts
+ useEffect(() => {
+ if (gameStarted) {
+ console.log('đź Game started, navigating to /game')
+ navigate("/game")
+ }
+ }, [gameStarted, navigate])
+
const handleExit = () => {
if (window.confirm("Biztosan ki szeretnél lépni a lobbyból?")) {
+ localStorage.removeItem('gameToken')
navigate("/home")
}
}
+ const handleStartGame = async () => {
+ try {
+ // Get gameId from gameState
+ const gameId = gameState?.gameId
+ if (!gameId) {
+ alert('Hiba: JĂĄtĂ©k azonosĂtĂł nem talĂĄlhatĂł')
+ return
+ }
+
+ console.log('Starting game with ID:', gameId)
+ const response = await startGame(gameId)
+ console.log('Game start response:', response)
+
+ // Backend will broadcast game:started event to all players
+ // Navigate to game page
+ navigate("/game")
+ } catch (error) {
+ console.error('Failed to start game:', error)
+ alert(`Nem sikerĂŒlt elindĂtani a jĂĄtĂ©kot: ${error.response?.data?.error || error.message}`)
+ }
+ }
+
+ const copyGameCode = () => {
+ navigator.clipboard.writeText(gameCode)
+ alert('Jåték kód vågólapra måsolva: ' + gameCode)
+ }
+
const getInitials = (name) => {
return name
.split(" ")
@@ -57,31 +119,121 @@ const Lobby = () => {
style={{ background: "rgba(0,0,0,0.25)" }}
>
- {user} Lobby-ja
+ Jåték Lobby
-
- Jåtékosok, akik csatlakoztak ehhez a szobåhoz:
+ {/* Game Code Display */}
+
+
+ Jåték Kód:
+
+
+
+ {gameCode}
+
+
+
+
+ Oszd meg ezt a kódot måsokkal, hogy csatlakozhassanak a jåtékhoz!
+
+
+
+ {/* Connection Status */}
+
+
+ {isConnected ? 'đą KapcsolĂłdva' : 'đŽ Kapcsolat megszakadt'}
+
+
+
+
+ Jåtékosok ({currentPlayers.length}):
-
+ {/* Role indicator */}
+
+ {isGamemaster ? (
+
+
đ Te vagy a Gamemaster!
+
Te nem jĂĄtszol, csak indĂtod Ă©s moderĂĄlod a jĂĄtĂ©kot.
+
+ ) : (
+
+
đź Te vagy egy JĂĄtĂ©kos!
+
VĂĄrj, amĂg a gamemaster elindĂtja a jĂĄtĂ©kot.
+
+ )}
+
+
+
+ {isGamemaster ? (
+ /* Gamemaster view - can start game */
+
+ ) : (
+ /* Player view - cannot start game, just wait */
+
+
VĂĄrakozĂĄs a gamemaster-re...
+
Csak a gamemaster indĂthatja el a jĂĄtĂ©kot
+
+ )}
diff --git a/SerpentRace_Frontend/src/pages/Game/PlayerSetup.jsx b/SerpentRace_Frontend/src/pages/Game/PlayerSetup.jsx
index 884b4516..c2c90e8d 100644
--- a/SerpentRace_Frontend/src/pages/Game/PlayerSetup.jsx
+++ b/SerpentRace_Frontend/src/pages/Game/PlayerSetup.jsx
@@ -1,4 +1,4 @@
-import React, { useState } from "react"
+import React, { useState, useEffect } from "react"
import { useNavigate, useLocation } from "react-router-dom"
import Navbar from "../../components/Navbar/Navbar.jsx"
import Background from "../../assets/backgrounds/Background.jsx"
@@ -6,6 +6,7 @@ import Footer from "../../components/Footer/Footer.jsx"
import useRequireAuth from "../../hooks/useRequireAuth.jsx"
import ButtonGreen from "../../components/Buttons/ButtonGreen.jsx"
import { motion } from "framer-motion"
+import { createGame, joinGame } from "../../api/gameApi.js"
const GameLobbySetup = () => {
const [username] = useRequireAuth({ key: "username", redirectTo: "/login" })
@@ -16,19 +17,82 @@ const GameLobbySetup = () => {
const [maxPlayers, setMaxPlayers] = useState(4)
const [isPublic, setIsPublic] = useState(true)
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState(null)
+ const [createdGameCode, setCreatedGameCode] = useState('')
+ const [showSuccess, setShowSuccess] = useState(false)
- const handleCreateLobby = () => {
- console.log({
- deckIds,
- maxPlayers,
- isPublic,
- })
- // Itt kĂŒldd el az API-nak a lobby lĂ©trehozĂĄsĂĄt
- // navigate("/game-lobby", { state: { lobbyId: response.lobbyId } })
+ const handleCreateLobby = async () => {
+ setLoading(true)
+ setError(null)
+
+ try {
+ const username = localStorage.getItem('username')
+
+ console.log('Creating game - username:', username)
+
+ if (!username) {
+ setError('KĂ©rlek jelentkezz be elĆször!')
+ setLoading(false)
+ return
+ }
+
+ // Backend expects deckids (array), maxplayers (number), logintype (0=PUBLIC, 1=PRIVATE)
+ const gameData = {
+ deckids: deckIds, // Array of deck UUIDs
+ maxplayers: maxPlayers, // Number
+ logintype: isPublic ? 0 : 1, // 0=PUBLIC, 1=PRIVATE, 2=ORGANIZATION
+ }
+
+ console.log('Creating game with data:', gameData)
+ const response = await createGame(gameData)
+ console.log('Game created:', response)
+
+ // Verify localStorage still has username
+ console.log('After create - username:', localStorage.getItem('username'))
+
+ // Backend returns game object directly
+ const code = response.gamecode || response.gameCode
+ if (code) {
+ setCreatedGameCode(code)
+ setShowSuccess(true)
+ }
+
+ // Creator needs to join their own game to get a gameToken
+ // This allows the WebSocket to recognize them as the gamemaster
+ try {
+ const username = localStorage.getItem('username')
+ const joinResponse = await joinGame({
+ gameCode: code,
+ playerName: username
+ })
+
+ if (joinResponse.gameToken) {
+ localStorage.setItem('gameToken', joinResponse.gameToken)
+ console.log('Creator joined game as gamemaster, token stored')
+ }
+ } catch (joinError) {
+ console.error('Failed to join game as creator:', joinError)
+ // Continue anyway - the creator can still try to join manually
+ }
+
+ // Wait 3 seconds to show code, then navigate to lobby
+ setTimeout(() => {
+ console.log('Navigating to lobby with code:', code)
+ navigate('/lobby', { state: { gameCode: code } })
+ }, 3000)
+ } catch (err) {
+ console.error('Create game error:', err)
+ console.error('Error response:', err.response?.data)
+ console.error('Error status:', err.response?.status)
+ setError(err.response?.data?.message || err.response?.data?.error || 'Nem sikerĂŒlt lĂ©trehozni a jĂĄtĂ©kot')
+ } finally {
+ setLoading(false)
+ }
}
if (deckIds.length === 0) {
- navigate("/choose-deck")
+ navigate("/choosedeck")
return null
}
@@ -67,6 +131,27 @@ const GameLobbySetup = () => {
{deckIds.length} pakli kivålasztva. Add meg a jåték részleteit.
+ {error && (
+
+ {error}
+
+ )}
+
+ {createdGameCode && (
+
+
JĂĄtĂ©k LĂ©trehozva! đ
+
+ {createdGameCode}
+
+
+ Oszd meg ezt a kódot mås jåtékosokkal, hogy csatlakozhassanak!
+
+
+ ĂtirĂĄnyĂtĂĄs a lobby-hoz 3 mĂĄsodperc mĂșlva...
+
+
+ )}
+
{/* Max Players */}
@@ -115,11 +200,17 @@ const GameLobbySetup = () => {
navigate("/choose-deck")}
+ onClick={() => navigate("/choosedeck")}
width="w-auto px-8"
className="bg-gray-600 hover:bg-gray-700"
+ disabled={loading}
+ />
+
-
diff --git a/SerpentRace_Frontend/src/pages/Landing/Home.jsx b/SerpentRace_Frontend/src/pages/Landing/Home.jsx
index 5dc095bc..b0da054a 100644
--- a/SerpentRace_Frontend/src/pages/Landing/Home.jsx
+++ b/SerpentRace_Frontend/src/pages/Landing/Home.jsx
@@ -1,24 +1,75 @@
// src/pages/Home/Home.jsx
// Régi PlayMenu-s oldal, "Home" néven
-import { useEffect } from "react"
+import { useEffect, useState } from "react"
+import { useNavigate } from "react-router-dom"
import useRequireAuth from "../../hooks/useRequireAuth"
import Navbar from "../../components/Navbar/Navbar"
import Footer from "../../components/Footer/Footer.jsx"
import Background from "../../assets/backgrounds/Background.jsx"
import PlayMenu from "../../components/Landingpage/PlayMenu.jsx"
+import { joinGame } from "../../api/gameApi.js"
export default function Home() {
+ const navigate = useNavigate()
// a hook inicializålja a user-t a localStorage-ból és visszaadja a state-et + settert
const [user, setUser] = useRequireAuth({ redirect: false }) // no redirect on unauthenticated visitors
+ const [isJoining, setIsJoining] = useState(false)
- // Dummy callbackok és user példa
- const handleJoinGame = (code) => {
- alert(`Csatlakozås jåtékhoz: ${code}`)
+ // Join game handler - csatlakozås jåtékhoz kóddal
+ const handleJoinGame = async (code) => {
+ if (!user) {
+ alert('KĂ©rlek elĆször jelentkezz be!')
+ navigate('/login')
+ return
+ }
+
+ console.log('=== JOIN GAME DEBUG ===')
+ console.log('Current user:', user)
+ console.log('Game code:', code)
+ console.log('LocalStorage username:', localStorage.getItem('username'))
+ console.log('LocalStorage authLevel:', localStorage.getItem('authLevel'))
+ console.log('======================')
+
+ setIsJoining(true)
+ try {
+ const joinData = {
+ gameCode: code.toUpperCase(),
+ playerName: user || 'Player',
+ }
+
+ console.log('Sending join request with:', joinData)
+ const response = await joinGame(joinData)
+ console.log('Joined game:', response)
+
+ // Backend returns game object directly
+ if (response.gameToken) {
+ localStorage.setItem('gameToken', response.gameToken)
+ }
+
+ navigate('/lobby', { state: { gameCode: code.toUpperCase() } })
+ } catch (err) {
+ const errorMsg = err.response?.data?.error || err.response?.data?.message || 'Nem sikerĂŒlt csatlakozni a jĂĄtĂ©khoz'
+ alert(errorMsg)
+ console.error('Join game error:', err)
+ console.error('Error details:', err.response?.data)
+ } finally {
+ setIsJoining(false)
+ }
}
+
+ // Create game handler - Ășj jĂĄtĂ©k lĂ©trehozĂĄsa
const handleCreateGame = () => {
- alert("Ăj jĂĄtĂ©k lĂ©trehozĂĄsa")
+ if (!user) {
+ alert('KĂ©rlek elĆször jelentkezz be!')
+ navigate('/login')
+ return
+ }
+
+ // Navigate to choose deck page to start game creation flow
+ navigate('/choosedeck')
}
+
const userObj = { name: user }
// ha szĂŒksĂ©ges a user mĂłdosĂtĂĄsa mĂĄshol: setUser("ĂșjnĂ©v") automatikusan menti localStorage-be
diff --git a/SerpentRace_Frontend/vite.config.js b/SerpentRace_Frontend/vite.config.js
index bff480b0..59f06db4 100644
--- a/SerpentRace_Frontend/vite.config.js
+++ b/SerpentRace_Frontend/vite.config.js
@@ -13,6 +13,17 @@ export default defineConfig({
},
hmr: {
clientPort: 5173,
+ },
+ proxy: {
+ '/api': {
+ target: 'http://backend:3000',
+ changeOrigin: true,
+ },
+ '/socket.io': {
+ target: 'http://backend:3000',
+ changeOrigin: true,
+ ws: true,
+ }
}
},
preview: {