feat!(DevWeb):Add timer for games

/!\ Warning: Need to rebuild the DB
This commit is contained in:
Lucàs
2024-05-01 14:36:18 +02:00
parent d54de92ce9
commit 0741b5ce94
7 changed files with 112 additions and 41 deletions
+18 -17
View File
@@ -4,31 +4,32 @@ USE ${database};
-- Table: User -- Table: User
CREATE TABLE IF NOT EXISTS `user` CREATE TABLE IF NOT EXISTS `user`
( (
id INT NOT NULL AUTO_INCREMENT, id INT NOT NULL AUTO_INCREMENT,
username VARCHAR(255) NOT NULL UNIQUE, username VARCHAR(255) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE, email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL,
gender VARCHAR(255) NOT NULL, gender VARCHAR(255) NOT NULL,
birth DATE NOT NULL, birth DATE NOT NULL,
PRIMARY KEY (id) PRIMARY KEY (id)
); );
-- Table: Game -- Table: Game
CREATE TABLE IF NOT EXISTS game CREATE TABLE IF NOT EXISTS game
( (
id INT NOT NULL AUTO_INCREMENT, id INT NOT NULL AUTO_INCREMENT,
difficulty VARCHAR(255) NOT NULL, difficulty VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
nb_rounds INT NOT NULL, nb_rounds INT NOT NULL,
nb_colors INT NOT NULL, nb_colors INT NOT NULL,
nb_values_per_color INT NOT NULL, nb_values_per_color INT NOT NULL,
timer INT NOT NULL,
PRIMARY KEY (id) PRIMARY KEY (id)
); );
-- Table: Player -- Table: Player
CREATE TABLE IF NOT EXISTS player CREATE TABLE IF NOT EXISTS player
( (
id INT NOT NULL AUTO_INCREMENT, id INT NOT NULL AUTO_INCREMENT,
game_id INT NOT NULL, game_id INT NOT NULL,
user_id INT NOT NULL, user_id INT NOT NULL,
@@ -47,10 +48,10 @@ CREATE TABLE IF NOT EXISTS player
-- Table: RecoveryPasswordToken -- Table: RecoveryPasswordToken
CREATE TABLE IF NOT EXISTS recovery_password_token CREATE TABLE IF NOT EXISTS recovery_password_token
( (
id INT NOT NULL AUTO_INCREMENT, id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL, user_id INT NOT NULL,
token VARCHAR(255) NOT NULL, token VARCHAR(255) NOT NULL,
expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id), PRIMARY KEY (id),
FOREIGN KEY (user_id) REFERENCES `user` (id) FOREIGN KEY (user_id) REFERENCES `user` (id)
); );
@@ -69,7 +69,7 @@ public class Game implements Serializable {
@Transient @Transient
private Deck deck; private Deck deck;
@Transient @Column(name = "timer")
private int timer; private int timer;
@Transient @Transient
@@ -90,6 +90,7 @@ public class Game implements Serializable {
public Game() { public Game() {
this.createdAt = new Date(); this.createdAt = new Date();
this.players = new ArrayList<>(); this.players = new ArrayList<>();
this.timer = 0;
} }
/** /**
@@ -4,5 +4,6 @@ public enum ClickChoice {
COLOR_VALUE, COLOR_VALUE,
COLOR, COLOR,
VALUE, VALUE,
NONE NONE,
TIMER_END
} }
@@ -16,7 +16,7 @@ public class SimpleGame {
this.id = game.getId().intValue(); this.id = game.getId().intValue();
this.players = new ArrayList<>(); this.players = new ArrayList<>();
for (Player p : playerArrayList) players.add(new SimplePlayer(p, game.getCurrentRound())); 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(); this.currentRound = game.getCurrentRound();
} }
@@ -21,7 +21,7 @@ public class SimplePlayer {
this.rightClickCount = player.getRightClickCount(); this.rightClickCount = player.getRightClickCount();
this.rapidClickCount = player.getRapidClickCount(); 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) { public SimplePlayer(Player player) {
@@ -12,6 +12,8 @@ import jakarta.websocket.server.ServerEndpoint;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import uppa.project.bean.PlayerBean; import uppa.project.bean.PlayerBean;
import uppa.project.database.dao.DAO; import uppa.project.database.dao.DAO;
import uppa.project.database.dao.DAOException; 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.SimpleGame;
import uppa.project.json.websocket.SimplePlayer; import uppa.project.json.websocket.SimplePlayer;
import uppa.project.json.websocket.SimpleUser; import uppa.project.json.websocket.SimpleUser;
import uppa.project.utils.GameProvider;
@ServerEndpoint(value = "/ws/game/{game_id}") @ServerEndpoint(value = "/ws/game/{game_id}")
public class GameWS { public class GameWS {
private static final HashMap<Game, ArrayList<Player>> games = new HashMap<>(); private static final HashMap<Game, ArrayList<Player>> games = new HashMap<>();
private static final HashMap<Game, Timer> timers = new HashMap<>();
Gson gson = new Gson(); Gson gson = new Gson();
private Game game; private Game game;
private Player player; private Player player;
@OnOpen @OnOpen
public void onOpen(Session session, @PathParam("game_id") String gameId) throws DAOException { public void onOpen(Session session, @PathParam("game_id") String gameId) throws DAOException {
this.game = GameProvider.getGame(Integer.parseInt(gameId)); DAO<Game> gameDAO = new Game_JPA_DAO_Factory().getDAOGame();
this.game = gameDAO.findById(Integer.parseInt(gameId));
if (!games.containsKey(game)) games.put(game, new ArrayList<>()); if (!games.containsKey(game)) games.put(game, new ArrayList<>());
} }
@@ -84,7 +87,18 @@ public class GameWS {
if (message.getType().equals("start")) { if (message.getType().equals("start")) {
game.setGameState(Game.GameState.STARTED); game.setGameState(Game.GameState.STARTED);
broadcast(new Message("start", gson.toJson(new SimpleGame(game, games.get(game)))).toJson()); 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")) { if (message.getType().equals("click")) {
@@ -151,6 +165,7 @@ public class GameWS {
player.setScore(playerScore - 1); player.setScore(playerScore - 1);
} }
} }
case TIMER_END -> {}
} }
} else { } else {
int nbSameCard = countSameCard(gameCard, games.get(game), game.getCurrentRound()); int nbSameCard = countSameCard(gameCard, games.get(game), game.getCurrentRound());
@@ -187,13 +202,14 @@ public class GameWS {
} }
} }
case NONE -> { case NONE -> {
if ((nbNone > nbSameCard) && (nbNone >= nbSameColor) && (nbNone > nbSameValue)){ if ((nbNone > nbSameCard) && (nbNone >= nbSameColor) && (nbNone > nbSameValue)) {
player.incrementRightClickCount(); player.incrementRightClickCount();
player.setScore(playerScore + 2); player.setScore(playerScore + 2);
} else { } else {
player.setScore(playerScore - 1); player.setScore(playerScore - 1);
} }
} }
case TIMER_END -> {}
} }
} }
@@ -205,6 +221,8 @@ public class GameWS {
// If all players have clicked // If all players have clicked
if (gameClickCount >= games.get(game).size()) { if (gameClickCount >= games.get(game).size()) {
// Stop the timer if it's running
timers.get(game).cancel();
// Reset the current click for all players // Reset the current click for all players
for (Player p : games.get(game)) p.setCurrentClick(null); for (Player p : games.get(game)) p.setCurrentClick(null);
@@ -223,7 +241,16 @@ public class GameWS {
// Check if the game is over // Check if the game is over
if (game.nextRound() || (second.getScore() == theoricWinner.getScore() && second.getRapidClickCount() == theoricWinner.getRapidClickCount())) { if (game.nextRound() || (second.getScore() == theoricWinner.getScore() && second.getRapidClickCount() == theoricWinner.getRapidClickCount())) {
broadcast(new Message("nextRound", gson.toJson(new SimpleGame(game, games.get(game)))).toJson()); 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 { } else {
theoricWinner.setWinner(); theoricWinner.setWinner();
@@ -231,11 +258,10 @@ public class GameWS {
em.getTransaction().begin(); em.getTransaction().begin();
for (Player p : games.get(game)) { for (Player p : games.get(game)) {
PlayerBean playerBean = new PlayerBean(p); PlayerBean playerBean = new PlayerBean(p);
if (playerBean.validate()) System.out.println("Player "+p.getUser().getUsername()+" sauvegardé en base de données"); if (playerBean.validate()) System.out.println("Player " + p.getUser().getUsername() + " sauvegardé en base de données");
else System.out.println(""); else System.out.println();
}; }
em.getTransaction().commit(); em.getTransaction().commit();
em.close();
// Broadcast the end of the game // Broadcast the end of the game
broadcast(new Message("end", gson.toJson(new SimpleGame(game, games.get(game)))).toJson()); 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 * 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 * @param currentRound manche courante
* @return nombre de cartes identiques à celle du plateau * @return nombre de cartes identiques à celle du plateau
*/ */
@@ -256,7 +283,7 @@ public class GameWS {
for (Player player : players) { for (Player player : players) {
Card card = player.getDeck().getCards().get(currentRound); Card card = player.getDeck().getCards().get(currentRound);
if (gameCard.equals(card)) { if (gameCard.equals(card)) {
counter ++; counter++;
} }
} }
return 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 * Retourne le nombre de joueurs avec une carte avec seulement la couleur correspondante à celle du plateau
*
* @param gameCard * @param gameCard
* @param players * @param players
* @param currentRound * @param currentRound
@@ -274,7 +302,7 @@ public class GameWS {
for (Player player : players) { for (Player player : players) {
Card card = player.getDeck().getCards().get(currentRound); Card card = player.getDeck().getCards().get(currentRound);
if (gameCard.getColor().equals(card.getColor()) && !gameCard.getValue().equals(card.getValue())) { if (gameCard.getColor().equals(card.getColor()) && !gameCard.getValue().equals(card.getValue())) {
counter ++; counter++;
} }
} }
return 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 * 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 * @param currentRound manche courante
* @return nombre de valeurs identiques à celle du plateau * @return nombre de valeurs identiques à celle du plateau
*/ */
@@ -292,7 +321,7 @@ public class GameWS {
for (Player player : players) { for (Player player : players) {
Card card = player.getDeck().getCards().get(currentRound); Card card = player.getDeck().getCards().get(currentRound);
if (gameCard.getValue().equals(card.getValue()) && !gameCard.getColor().equals(card.getColor())) { if (gameCard.getValue().equals(card.getValue()) && !gameCard.getColor().equals(card.getColor())) {
counter ++; counter++;
} }
} }
return 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 * Retourne le nombre de joueurs avec une carte totalement différente de celle du plateau
*
* @param gameCard * @param gameCard
* @param players * @param players
* @param currentRound * @param currentRound
@@ -310,7 +340,7 @@ public class GameWS {
for (Player player : players) { for (Player player : players) {
Card card = player.getDeck().getCards().get(currentRound); Card card = player.getDeck().getCards().get(currentRound);
if (!gameCard.getColor().equals(card.getColor()) && !gameCard.getValue().equals(card.getValue())) { if (!gameCard.getColor().equals(card.getColor()) && !gameCard.getValue().equals(card.getValue())) {
counter ++; counter++;
} }
} }
return counter; return counter;
@@ -51,6 +51,7 @@
style="position: absolute; right: 0; z-index: 9999"> style="position: absolute; right: 0; z-index: 9999">
<div class="buttons is-flex-direction-column"> <div class="buttons is-flex-direction-column">
<p id="round" class="title has-text-white"></p> <p id="round" class="title has-text-white"></p>
<p id="timer" class="subtitle has-text-white"></p>
<button class="button is-fullwidth" data-value="COLOR_VALUE">Même couleur et valeur</button> <button class="button is-fullwidth" data-value="COLOR_VALUE">Même couleur et valeur</button>
<button class="button is-fullwidth" data-value="COLOR">Même couleur</button> <button class="button is-fullwidth" data-value="COLOR">Même couleur</button>
<button class="button is-fullwidth" data-value="VALUE">Même valeur</button> <button class="button is-fullwidth" data-value="VALUE">Même valeur</button>
@@ -212,6 +213,22 @@
import Card from "${pageContext.request.contextPath}/static/js/Card.js" import Card from "${pageContext.request.contextPath}/static/js/Card.js"
const choice = document.querySelector('#choice'); 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 => { choice.querySelectorAll('button').forEach(button => {
button.addEventListener('click', () => { button.addEventListener('click', () => {
@@ -221,6 +238,8 @@
} }
wsgame.ws.send(JSON.stringify(message)); wsgame.ws.send(JSON.stringify(message));
havePlayed = true;
// Disable buttons // Disable buttons
choice.querySelectorAll('button').forEach(button => button.disabled = true); choice.querySelectorAll('button').forEach(button => button.disabled = true);
}); });
@@ -255,6 +274,8 @@
document.querySelector('#gameWaiting').style.display = 'none'; document.querySelector('#gameWaiting').style.display = 'none';
document.querySelector('#gameStarted').style.display = 'block'; document.querySelector('#gameStarted').style.display = 'block';
timerInterval = setInterval(interval, 1000);
const deck = document.querySelector('#deck'); // Column const deck = document.querySelector('#deck'); // Column
const myCard = document.querySelector('#myCard'); // Column const myCard = document.querySelector('#myCard'); // Column
const otherCards = document.querySelector('#otherCards'); // Columns const otherCards = document.querySelector('#otherCards'); // Columns
@@ -267,7 +288,7 @@
round.innerText = ""; round.innerText = "";
// Show current round // Show current round
round.innerText = "Manche " + (currentGame.currentRound+1) round.innerText = "Manche " + (currentGame.currentRound + 1)
// Show other player cards // Show other player cards
game.players game.players
@@ -296,9 +317,16 @@
wsgame.onMessage("updatePlayer", (p) => { wsgame.onMessage("updatePlayer", (p) => {
document.querySelector(".player-" + p.user.id + " .card-play").style.boxShadow = "inset 0px 0px 30px 10px orange"; document.querySelector(".player-" + p.user.id + " .card-play").style.boxShadow = "inset 0px 0px 30px 10px orange";
}) })
wsgame.onMessage("end", (game) => { wsgame.onMessage("timerEnd", (game) => {
window.location.href = "${pageContext.request.contextPath}/game-statistics?id=${game.id}&endGame=true"; if (!havePlayed) {
const message = {
type: "click",
data: "TIMER_END"
}
wsgame.ws.send(JSON.stringify(message));
}
}) })
wsgame.onMessage("nextRound", (game) => { wsgame.onMessage("nextRound", (game) => {
currentGame = game; currentGame = game;
@@ -311,12 +339,17 @@
const otherCards = document.querySelector('#otherCards'); // Columns const otherCards = document.querySelector('#otherCards'); // Columns
const round = document.querySelector('#round'); const round = document.querySelector('#round');
clearInterval(timerInterval);
remainingTime = timer;
document.querySelector('#timer').innerText = "Temps restant: " + remainingTime + "s";
timerInterval = setInterval(interval, 1000);
// Reset content // Reset content
deck.innerHTML = ""; deck.innerHTML = "";
myCard.innerHTML = ""; myCard.innerHTML = "";
otherCards.innerHTML = ""; otherCards.innerHTML = "";
round.innerText = ""; round.innerText = "";
havePlayed = false;
choice.querySelectorAll('button').forEach(button => button.disabled = false); choice.querySelectorAll('button').forEach(button => button.disabled = false);
// Show the current round // Show the current round
@@ -347,6 +380,11 @@
deck.innerHTML = deckCard.render(); 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 // Close handling
wsgame.onClose(() => console.log("Disconnected from the server (GameWS)")); wsgame.onClose(() => console.log("Disconnected from the server (GameWS)"));