Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiplayer 2 #73

Merged
merged 4 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 38 additions & 9 deletions multiplayerservice/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,6 @@ function createParty() {
return partyCode;
}

// Join a party
function joinParty(partyCode, socket) {
if (parties[partyCode]) {
parties[partyCode].push(socket);
return true;
}
return false;
}

const updateLobbyUsers = (lobbyCode) => {
io.to(lobbyCode).emit('lobbyUsers', Object.values(lobby[lobbyCode]));
};
Expand All @@ -44,6 +35,30 @@ const broadcastQuestions = (partyCode, questions) => {
io.to(partyCode).emit('questionsUpdated', questions);
};

const playerFinished = (partyCode, socketId, totalPoints) => {
console.log(totalPoints)
lobby[partyCode][socketId].finished = true;
lobby[partyCode][socketId].totalPoints = totalPoints;

const allFinished = Object.values(lobby[partyCode]).every(player => player.finished);

if (allFinished) {
const playersWithPoints = Object.entries(lobby[partyCode]).map(([id, player]) => {
console.log(player)
return {
finished: player.finished,
username: player.username,
points: player.totalPoints
}});

// Sort the players by their total points in descending order
playersWithPoints.sort((a, b) => b.totalPoints - a.totalPoints);

// Emit an event to notify all clients with the ordered list of players and their points
io.to(partyCode).emit('allPlayersFinished', playersWithPoints);
}
};

io.on('connection', socket => {
console.log('Client connected');

Expand Down Expand Up @@ -79,6 +94,20 @@ io.on('connection', socket => {
broadcastQuestions(partyCode, questions);
});

socket.on('exitParty', (partyCode) => {
console.log("aqui")
if (lobby[partyCode]) {
// Remove the user from the lobby
delete lobby[partyCode][socket.id];
// Broadcast updated list of users to all remaining users in the lobby
updateLobbyUsers(partyCode);
}
});

socket.on('playerFinished', (partyCode, totalPoints) => {
playerFinished(partyCode, socket.id, totalPoints);
});

socket.on('disconnect', () => {
console.log('Client disconnected');
// Remove the user from the lobby when they disconnect
Expand Down
11 changes: 11 additions & 0 deletions webapp/src/components/game/LobbyGame.scss
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ table {
.button-container {
display: flex;
justify-content: space-between;
margin-top: 20px;
}

.start-game-button {
Expand All @@ -88,6 +89,16 @@ table {
margin-left: 10px;
}

.exit-lobby-button {
background-color: red;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
margin-left: 10px;
}

.start-game-button:disabled {
background-color: #cccccc; /* Change background color for disabled state */
color: #666666; /* Change text color for disabled state */
Expand Down
Empty file.
13 changes: 10 additions & 3 deletions webapp/src/components/game/ScoreboardGame.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { FC} from 'react'
import { Player } from './singleplayer/GameSinglePlayer';
import './ScoreboardGame.css';
import { PlayerWithPoints } from './multiplayer/GameMultiPlayer';

interface ScoreboardGameProps {
userScores: Player[];
userScoresSinglePlayer?: Player[];
userScoresMultiPlayer?: PlayerWithPoints[];
}


const ScoreboardGame:FC<ScoreboardGameProps> = ({userScores}) => {
const sorted = userScores.sort((a, b) => b.points - a.points);
const ScoreboardGame:FC<ScoreboardGameProps> = ({userScoresSinglePlayer, userScoresMultiPlayer}) => {
let sorted;
if(userScoresSinglePlayer){
sorted = userScoresSinglePlayer.sort((a, b) => b.points - a.points);
} else if (userScoresMultiPlayer){
sorted = userScoresMultiPlayer.sort((a, b) => b.points - a.points);
}
return (
<section>
<table>
Expand Down
23 changes: 15 additions & 8 deletions webapp/src/components/game/multiplayer/GameMultiPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Container } from '@mui/material';
import LobbyMultiPlayer from './LobbyMultiPlayer';
import { Question4Answers } from '../singleplayer/GameSinglePlayer';
import QuestionsMultiPlayer from './QuestionsMultiPlayer';
import ScoreboardGame from '../ScoreboardGame';

interface GameMultiPlayerProps {

Expand All @@ -22,7 +23,12 @@ export interface UserPlayer {
isAdmin: boolean;
}

const GameMultiPlayer: FC<GameMultiPlayerProps> = ({}) => {
export interface PlayerWithPoints {
username: string;
points: number;
}

const GameMultiPlayer: FC<GameMultiPlayerProps> = () => {

const SERVER_URL = 'http://localhost:8006';
const username = localStorage.getItem("username")
Expand All @@ -32,6 +38,7 @@ const GameMultiPlayer: FC<GameMultiPlayerProps> = ({}) => {
const [partyCode, setPartyCode] = useState<string>("");
const [users, setUsers] = useState<UserPlayer[]>([]);
const [questions, setQuestions] = useState<Question4Answers[]>([]);
const [sortedUsersByPoints, setSortedUsersByPoints] = useState<PlayerWithPoints[]>([])

useEffect(() => {
const newSocket = io(SERVER_URL);
Expand All @@ -43,24 +50,23 @@ const GameMultiPlayer: FC<GameMultiPlayerProps> = ({}) => {
});

newSocket.on('joinedParty', (user: UserPlayer) => {
console.log(user)
console.log(`User ${username} joined the party`);
setStage(2);
console.log(users)
})

newSocket.on('lobbyUsers', (users: UserPlayer[]) => {
setUsers(users);
console.log(users)
});

newSocket.on('partyNotFound', () => {
console.log('Party not found');
});

newSocket.on('allPlayersFinished', (playersWithPoints:PlayerWithPoints[]) => {
setSortedUsersByPoints(playersWithPoints);
setStage(4);
})

newSocket.on('questionsUpdated', (questions: Question4Answers[]) => {
console.log('questions recieved from server')
console.log(questions);
setQuestions(questions);
setStage(3);
})
Expand All @@ -82,7 +88,8 @@ const GameMultiPlayer: FC<GameMultiPlayerProps> = ({}) => {
<Container sx={{ mt: 9 }}>
{stage === 1 && <MenuMultiplayer socket={socket} handleCurrentStage={handleCurrentStage} handlePartyCode={handlePartyCode}/>}
{stage === 2 && <LobbyMultiPlayer socket={socket} handleCurrentStage={handleCurrentStage} partyCode={partyCode} users={users}/>}
{stage === 3 && <QuestionsMultiPlayer socket={socket} handleCurrentStage={handleCurrentStage} questions={questions}/>}
{stage === 3 && <QuestionsMultiPlayer socket={socket} handleCurrentStage={handleCurrentStage} questions={questions} partyCode={partyCode}/>}
{stage === 4 && <ScoreboardGame userScoresMultiPlayer={sortedUsersByPoints}/>}
</Container>
)
}
Expand Down
29 changes: 15 additions & 14 deletions webapp/src/components/game/multiplayer/LobbyMultiPlayer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC, useEffect, useState } from 'react'
import { FC, useState } from 'react'
import { SocketProps, UserPlayer } from './GameMultiPlayer';
import '../LobbyGame.scss';
import axios from 'axios';
Expand All @@ -14,28 +14,34 @@ const LobbyMultiPlayer: FC<LobbyMultiPlayerProps> = ({socket, handleCurrentStage

const [isFetched, setFetched] = useState<boolean>(true);

const uuid = localStorage.getItem("uuid");

const fetchQuestions = async () => {
setFetched(false)
const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000';

try {
console.log(users)
const requestData = {
players: users.map((user) => ({uuid: user.uuid}))
}
console.log("requestData")
console.log(requestData)
const response = await axios.post(`${apiEndpoint}/createGame`, requestData);

console.log("Juego creado")
console.log(response.data)
socket.emit('updateQuestions', partyCode, response.data);
setFetched(true);
} catch (error) {
console.error('Error al obtener las preguntas:', error);
}
};

const exitLobby = () => {
socket.emit('exitParty', partyCode)
handleCurrentStage(1)
}

const isAdmin = () => {
return users.some((user) => user.uuid === uuid && user.isAdmin);
}

return (
<div className='lobby-container'>
<h2 className='lobby-title'>Lobby - Multiplayer</h2>
Expand All @@ -50,14 +56,9 @@ const LobbyMultiPlayer: FC<LobbyMultiPlayerProps> = ({socket, handleCurrentStage
</div>
))}
<div className='button-container'>
{/* {isFetched && <button className="start-game-button" onClick={() => setCurrentStage(2)}>
Start Game
</button>}
{!isFetched && <button className="start-game-button" onClick={() => setCurrentStage(2)} disabled>
Loading questions...
</button>} */}
<button className="start-game-button" onClick={() => handleCurrentStage(3)}>Exit</button>
{isFetched && <button className="start-game-button" onClick={fetchQuestions}>Start game</button>}
<button className="exit-lobby-button" onClick={exitLobby}>Exit</button>
{isFetched && isAdmin() && <button className="start-game-button" onClick={fetchQuestions}>Start game</button>}
{isFetched && !isAdmin() && <button className="start-game-button" onClick={fetchQuestions} disabled>Start game</button>}
{!isFetched && <button className="start-game-button" disabled>Loading questions ...</button>}
</div>
</div>
Expand Down
3 changes: 0 additions & 3 deletions webapp/src/components/game/multiplayer/MenuMultiplayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ const MenuMultiplayer: FC<MenuMultiplayerProps> = ({socket, handleCurrentStage,
};

const joinParty = () => {
console.log("Joining party...")
console.log(typedCode)
console.log(username)
const user: UserPlayer = {
username: username,
totalPoints: parseInt(totalPoints),
Expand Down
85 changes: 82 additions & 3 deletions webapp/src/components/game/multiplayer/QuestionsMultiPlayer.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,94 @@
import { FC } from 'react'
import { FC, useState } from 'react'
import { SocketProps } from './GameMultiPlayer';
import { Question4Answers } from '../singleplayer/GameSinglePlayer';
import axios from 'axios';

interface QuestionsMultiPlayerProps {
socket: SocketProps;
handleCurrentStage: (n: number) => void
questions: Question4Answers[]
partyCode: string
}

const QuestionsMultiPlayer: FC<QuestionsMultiPlayerProps> = ({socket,handleCurrentStage,questions}) => {
return <div>QuestionsMultiPlayer</div>
const QuestionsMultiPlayer: FC<QuestionsMultiPlayerProps> = ({socket, questions, partyCode}) => {
const uuid = localStorage.getItem("userUUID");
const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000';

const [currentQuestion, setCurrentQuestion] = useState(0);
const [correctAnswers, setCorrectAnswers] = useState(0);

const handleAnswerClick = async (answer: string) => {
if(questions[currentQuestion].correctAnswer === answer){
setCorrectAnswers(correctAnswers + 1);

}
setCurrentQuestion(currentQuestion + 1);
if(currentQuestion+2 === questions.length){
const totalPoints = calculatePoints(correctAnswers, questions.length);
// the player has finished the game
// update stats for each player
const requestData ={ "players": [{
"uuid": uuid,
"nCorrectAnswers": correctAnswers,
"nWrongAnswers": questions.length - correctAnswers,
"totalScore": totalPoints,
"isWinner": false
}]}
await axios.post(`${apiEndpoint}/updateStats`, requestData);
// pass the points obtained of each player to the socket
socket.emit('playerFinished', partyCode, totalPoints)
}
};

const calculatePoints = (correctAnswers: number, totalQuestions: number) => {
const incorrectQuestions = totalQuestions - correctAnswers;
const points = correctAnswers * 100 - incorrectQuestions * 25;
return points;
}

const getShuffledAnswers = () => {
const answers = [
questions[currentQuestion].correctAnswer,
questions[currentQuestion].incorrectAnswer1,
questions[currentQuestion].incorrectAnswer2,
questions[currentQuestion].incorrectAnswer3,
];

for (let i = answers.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[answers[i], answers[j]] = [answers[j], answers[i]];
}

return answers;
};

return (
<div>
{(currentQuestion+1) < questions.length && (
<>
<h2>Question {currentQuestion + 1}</h2>
<p>{questions[currentQuestion].question}</p>
<div className="answer-grid">
{getShuffledAnswers().map((answer) => (
<button
key={answer}
onClick={() => handleAnswerClick(answer)}
>
{answer}
</button>
))}
</div>
</>
)}
{currentQuestion+1 === questions.length && (
<>
<p>You answered {correctAnswers} out of {questions.length} questions correctly.</p>
<p>You earned {calculatePoints(correctAnswers, questions.length)} points.</p>
<p>Waiting for the rest of the players to finish...</p>
</>
)}
</div>
)
}

export default QuestionsMultiPlayer
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const GameSinglePlayer = () => {
<Container sx={{ mt: 9 }}>
{currentStage === 1 && (<LobbyGame players={players} setPlayers={handlePlayers} setCurrentStage={handleCurrentStage} isFetched={fetched}/>)}
{currentStage === 2 && (<PlayingGame questions={questions} setCurrentStage={handleCurrentStage} setPlayers={handlePlayers} players={players}/>)}
{currentStage === 3 && (<ScoreboardGame userScores={players}/> )}
{currentStage === 3 && (<ScoreboardGame userScoresSinglePlayer={players}/> )}
</Container>
);
};
Expand Down
Loading