Skip to content

Commit

Permalink
Merge pull request #73 from Arquisoft/multiplayer2
Browse files Browse the repository at this point in the history
Multiplayer 2
  • Loading branch information
pelazas authored Apr 3, 2024
2 parents 7305b10 + 602534a commit 77a5acd
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 41 deletions.
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

0 comments on commit 77a5acd

Please sign in to comment.