From 0741b5ce94817bf7f1c56d8b7093ea370f99aabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luc=C3=A0s?= Date: Wed, 1 May 2024 14:36:18 +0200 Subject: [PATCH] feat!(DevWeb):Add timer for games /!\ Warning: Need to rebuild the DB --- S2/DevWeb/Projet/mysql/template.sql | 35 +++++----- .../java/uppa/project/database/pojo/Game.java | 3 +- .../project/json/websocket/ClickChoice.java | 3 +- .../project/json/websocket/SimpleGame.java | 2 +- .../project/json/websocket/SimplePlayer.java | 2 +- .../uppa/project/web/websocket/GameWS.java | 64 ++++++++++++++----- .../src/main/webapp/WEB-INF/pages/game.jsp | 44 ++++++++++++- 7 files changed, 112 insertions(+), 41 deletions(-) diff --git a/S2/DevWeb/Projet/mysql/template.sql b/S2/DevWeb/Projet/mysql/template.sql index f00c49f..d1400a2 100644 --- a/S2/DevWeb/Projet/mysql/template.sql +++ b/S2/DevWeb/Projet/mysql/template.sql @@ -4,31 +4,32 @@ USE ${database}; -- Table: User CREATE TABLE IF NOT EXISTS `user` ( - id INT NOT NULL AUTO_INCREMENT, - username VARCHAR(255) NOT NULL UNIQUE, - email VARCHAR(255) NOT NULL UNIQUE, - password VARCHAR(255) NOT NULL, - gender VARCHAR(255) NOT NULL, - birth DATE NOT NULL, + id INT NOT NULL AUTO_INCREMENT, + username VARCHAR(255) NOT NULL UNIQUE, + email VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + gender VARCHAR(255) NOT NULL, + birth DATE NOT NULL, PRIMARY KEY (id) ); -- Table: Game CREATE TABLE IF NOT EXISTS game ( - id INT NOT NULL AUTO_INCREMENT, - difficulty VARCHAR(255) NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - nb_rounds INT NOT NULL, - nb_colors INT NOT NULL, - nb_values_per_color INT NOT NULL, + id INT NOT NULL AUTO_INCREMENT, + difficulty VARCHAR(255) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + nb_rounds INT NOT NULL, + nb_colors INT NOT NULL, + nb_values_per_color INT NOT NULL, + timer INT NOT NULL, PRIMARY KEY (id) ); -- Table: Player CREATE TABLE IF NOT EXISTS player ( - id INT NOT NULL AUTO_INCREMENT, + id INT NOT NULL AUTO_INCREMENT, game_id INT NOT NULL, user_id INT NOT NULL, @@ -47,10 +48,10 @@ CREATE TABLE IF NOT EXISTS player -- Table: RecoveryPasswordToken CREATE TABLE IF NOT EXISTS recovery_password_token ( - id INT NOT NULL AUTO_INCREMENT, - user_id INT NOT NULL, - token VARCHAR(255) NOT NULL, - expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + id INT NOT NULL AUTO_INCREMENT, + user_id INT NOT NULL, + token VARCHAR(255) NOT NULL, + expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), FOREIGN KEY (user_id) REFERENCES `user` (id) ); diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/database/pojo/Game.java b/S2/DevWeb/Projet/src/main/java/uppa/project/database/pojo/Game.java index 4373cf4..ea91bfa 100644 --- a/S2/DevWeb/Projet/src/main/java/uppa/project/database/pojo/Game.java +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/database/pojo/Game.java @@ -69,7 +69,7 @@ public class Game implements Serializable { @Transient private Deck deck; - @Transient + @Column(name = "timer") private int timer; @Transient @@ -90,6 +90,7 @@ public class Game implements Serializable { public Game() { this.createdAt = new Date(); this.players = new ArrayList<>(); + this.timer = 0; } /** diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/ClickChoice.java b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/ClickChoice.java index 35b0aeb..5eada04 100644 --- a/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/ClickChoice.java +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/ClickChoice.java @@ -4,5 +4,6 @@ public enum ClickChoice { COLOR_VALUE, COLOR, VALUE, - NONE + NONE, + TIMER_END } diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleGame.java b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleGame.java index 076dfc5..f565f99 100644 --- a/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleGame.java +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimpleGame.java @@ -16,7 +16,7 @@ public class SimpleGame { this.id = game.getId().intValue(); this.players = new ArrayList<>(); for (Player p : playerArrayList) players.add(new SimplePlayer(p, game.getCurrentRound())); - this.currentCard = game.getDeck().getCards().get(game.getCurrentRound()); + this.currentCard = game.getDeck().getCards().get(game.getCurrentRound() % game.getDeck().getCards().size()); this.currentRound = game.getCurrentRound(); } diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimplePlayer.java b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimplePlayer.java index 88cb454..b49c04d 100644 --- a/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimplePlayer.java +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/json/websocket/SimplePlayer.java @@ -21,7 +21,7 @@ public class SimplePlayer { this.rightClickCount = player.getRightClickCount(); this.rapidClickCount = player.getRapidClickCount(); - this.currentCard = player.getDeck().getCards().get(currentRound); + this.currentCard = player.getDeck().getCards().get(currentRound % player.getDeck().getCards().size()); } public SimplePlayer(Player player) { diff --git a/S2/DevWeb/Projet/src/main/java/uppa/project/web/websocket/GameWS.java b/S2/DevWeb/Projet/src/main/java/uppa/project/web/websocket/GameWS.java index e27b6e4..ba811e7 100644 --- a/S2/DevWeb/Projet/src/main/java/uppa/project/web/websocket/GameWS.java +++ b/S2/DevWeb/Projet/src/main/java/uppa/project/web/websocket/GameWS.java @@ -12,6 +12,8 @@ import jakarta.websocket.server.ServerEndpoint; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Timer; +import java.util.TimerTask; import uppa.project.bean.PlayerBean; import uppa.project.database.dao.DAO; import uppa.project.database.dao.DAOException; @@ -27,19 +29,20 @@ import uppa.project.json.websocket.Message; import uppa.project.json.websocket.SimpleGame; import uppa.project.json.websocket.SimplePlayer; import uppa.project.json.websocket.SimpleUser; -import uppa.project.utils.GameProvider; @ServerEndpoint(value = "/ws/game/{game_id}") public class GameWS { private static final HashMap> games = new HashMap<>(); + private static final HashMap timers = new HashMap<>(); Gson gson = new Gson(); private Game game; private Player player; @OnOpen public void onOpen(Session session, @PathParam("game_id") String gameId) throws DAOException { - this.game = GameProvider.getGame(Integer.parseInt(gameId)); + DAO gameDAO = new Game_JPA_DAO_Factory().getDAOGame(); + this.game = gameDAO.findById(Integer.parseInt(gameId)); if (!games.containsKey(game)) games.put(game, new ArrayList<>()); } @@ -84,7 +87,18 @@ public class GameWS { if (message.getType().equals("start")) { game.setGameState(Game.GameState.STARTED); broadcast(new Message("start", gson.toJson(new SimpleGame(game, games.get(game)))).toJson()); - // TODO Start Timer + + // TODO Start the timer + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + // Broadcast the end of the game + broadcast(new Message("timerEnd", gson.toJson(new SimpleGame(game, games.get(game)))).toJson()); + timer.cancel(); + } + }, game.getTimer() * 1000L); + timers.put(game, timer); } if (message.getType().equals("click")) { @@ -151,6 +165,7 @@ public class GameWS { player.setScore(playerScore - 1); } } + case TIMER_END -> {} } } else { int nbSameCard = countSameCard(gameCard, games.get(game), game.getCurrentRound()); @@ -187,13 +202,14 @@ public class GameWS { } } case NONE -> { - if ((nbNone > nbSameCard) && (nbNone >= nbSameColor) && (nbNone > nbSameValue)){ + if ((nbNone > nbSameCard) && (nbNone >= nbSameColor) && (nbNone > nbSameValue)) { player.incrementRightClickCount(); player.setScore(playerScore + 2); } else { player.setScore(playerScore - 1); } } + case TIMER_END -> {} } } @@ -205,6 +221,8 @@ public class GameWS { // If all players have clicked if (gameClickCount >= games.get(game).size()) { + // Stop the timer if it's running + timers.get(game).cancel(); // Reset the current click for all players for (Player p : games.get(game)) p.setCurrentClick(null); @@ -223,7 +241,16 @@ public class GameWS { // Check if the game is over if (game.nextRound() || (second.getScore() == theoricWinner.getScore() && second.getRapidClickCount() == theoricWinner.getRapidClickCount())) { broadcast(new Message("nextRound", gson.toJson(new SimpleGame(game, games.get(game)))).toJson()); - // TODO Start Timer + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + // Broadcast the end of the game + broadcast(new Message("timerEnd", gson.toJson(new SimpleGame(game, games.get(game)))).toJson()); + timer.cancel(); + } + }, game.getTimer() * 1000L); + timers.put(game, timer); } else { theoricWinner.setWinner(); @@ -231,11 +258,10 @@ public class GameWS { em.getTransaction().begin(); for (Player p : games.get(game)) { PlayerBean playerBean = new PlayerBean(p); - if (playerBean.validate()) System.out.println("Player "+p.getUser().getUsername()+" sauvegardé en base de données"); - else System.out.println(""); - }; + if (playerBean.validate()) System.out.println("Player " + p.getUser().getUsername() + " sauvegardé en base de données"); + else System.out.println(); + } em.getTransaction().commit(); - em.close(); // Broadcast the end of the game broadcast(new Message("end", gson.toJson(new SimpleGame(game, games.get(game)))).toJson()); @@ -246,8 +272,9 @@ public class GameWS { /** * Retourne le nombre de joueurs avec une carte identique à celle du plateau - * @param gameCard carte du plateau - * @param players liste des joueurs + * + * @param gameCard carte du plateau + * @param players liste des joueurs * @param currentRound manche courante * @return nombre de cartes identiques à celle du plateau */ @@ -256,7 +283,7 @@ public class GameWS { for (Player player : players) { Card card = player.getDeck().getCards().get(currentRound); if (gameCard.equals(card)) { - counter ++; + counter++; } } return counter; @@ -264,6 +291,7 @@ public class GameWS { /** * Retourne le nombre de joueurs avec une carte avec seulement la couleur correspondante à celle du plateau + * * @param gameCard * @param players * @param currentRound @@ -274,7 +302,7 @@ public class GameWS { for (Player player : players) { Card card = player.getDeck().getCards().get(currentRound); if (gameCard.getColor().equals(card.getColor()) && !gameCard.getValue().equals(card.getValue())) { - counter ++; + counter++; } } return counter; @@ -282,8 +310,9 @@ public class GameWS { /** * Retourne le nombre de joueurs avec une carte avec seulement la valeur correspondante à celle du plateau - * @param gameCard carte du plateau - * @param players liste des joueurs + * + * @param gameCard carte du plateau + * @param players liste des joueurs * @param currentRound manche courante * @return nombre de valeurs identiques à celle du plateau */ @@ -292,7 +321,7 @@ public class GameWS { for (Player player : players) { Card card = player.getDeck().getCards().get(currentRound); if (gameCard.getValue().equals(card.getValue()) && !gameCard.getColor().equals(card.getColor())) { - counter ++; + counter++; } } return counter; @@ -300,6 +329,7 @@ public class GameWS { /** * Retourne le nombre de joueurs avec une carte totalement différente de celle du plateau + * * @param gameCard * @param players * @param currentRound @@ -310,7 +340,7 @@ public class GameWS { for (Player player : players) { Card card = player.getDeck().getCards().get(currentRound); if (!gameCard.getColor().equals(card.getColor()) && !gameCard.getValue().equals(card.getValue())) { - counter ++; + counter++; } } return counter; diff --git a/S2/DevWeb/Projet/src/main/webapp/WEB-INF/pages/game.jsp b/S2/DevWeb/Projet/src/main/webapp/WEB-INF/pages/game.jsp index 04f56fc..2a4c7d4 100644 --- a/S2/DevWeb/Projet/src/main/webapp/WEB-INF/pages/game.jsp +++ b/S2/DevWeb/Projet/src/main/webapp/WEB-INF/pages/game.jsp @@ -51,6 +51,7 @@ style="position: absolute; right: 0; z-index: 9999">

+

@@ -212,6 +213,22 @@ import Card from "${pageContext.request.contextPath}/static/js/Card.js" const choice = document.querySelector('#choice'); + let havePlayed = false; + + const timer = ${game.timer}; + let remainingTime = timer; + let timerInterval; + + // Display timer + document.querySelector('#timer').innerText = "Temps restant: " + remainingTime + "s"; + + const interval = () => { + remainingTime--; + + document.querySelector('#timer').innerText = "Temps restant: " + remainingTime + "s"; + + if (remainingTime <= 0) clearInterval(timerInterval); + } choice.querySelectorAll('button').forEach(button => { button.addEventListener('click', () => { @@ -221,6 +238,8 @@ } wsgame.ws.send(JSON.stringify(message)); + havePlayed = true; + // Disable buttons choice.querySelectorAll('button').forEach(button => button.disabled = true); }); @@ -255,6 +274,8 @@ document.querySelector('#gameWaiting').style.display = 'none'; document.querySelector('#gameStarted').style.display = 'block'; + timerInterval = setInterval(interval, 1000); + const deck = document.querySelector('#deck'); // Column const myCard = document.querySelector('#myCard'); // Column const otherCards = document.querySelector('#otherCards'); // Columns @@ -267,7 +288,7 @@ round.innerText = ""; // Show current round - round.innerText = "Manche " + (currentGame.currentRound+1) + round.innerText = "Manche " + (currentGame.currentRound + 1) // Show other player cards game.players @@ -296,9 +317,16 @@ wsgame.onMessage("updatePlayer", (p) => { document.querySelector(".player-" + p.user.id + " .card-play").style.boxShadow = "inset 0px 0px 30px 10px orange"; }) - wsgame.onMessage("end", (game) => { - window.location.href = "${pageContext.request.contextPath}/game-statistics?id=${game.id}&endGame=true"; + wsgame.onMessage("timerEnd", (game) => { + if (!havePlayed) { + const message = { + type: "click", + data: "TIMER_END" + } + wsgame.ws.send(JSON.stringify(message)); + } }) + wsgame.onMessage("nextRound", (game) => { currentGame = game; @@ -311,12 +339,17 @@ const otherCards = document.querySelector('#otherCards'); // Columns const round = document.querySelector('#round'); + clearInterval(timerInterval); + remainingTime = timer; + document.querySelector('#timer').innerText = "Temps restant: " + remainingTime + "s"; + timerInterval = setInterval(interval, 1000); // Reset content deck.innerHTML = ""; myCard.innerHTML = ""; otherCards.innerHTML = ""; round.innerText = ""; + havePlayed = false; choice.querySelectorAll('button').forEach(button => button.disabled = false); // Show the current round @@ -347,6 +380,11 @@ deck.innerHTML = deckCard.render(); }) + wsgame.onMessage("end", (game) => { + clearInterval(timerInterval); + window.location.href = "${pageContext.request.contextPath}/game-statistics?id=${game.id}&endGame=true"; + }) + // Close handling wsgame.onClose(() => console.log("Disconnected from the server (GameWS)"));