refacto - restructure code

This commit is contained in:
Kevin Mitressé
2024-06-14 17:05:30 +02:00
parent 4fa4cf2d1f
commit 2a4a594b14
162 changed files with 2 additions and 2 deletions
@@ -0,0 +1,84 @@
/*
* Card.java, 20/03/2024
* UPPA M1 TI 2023-2024
* Pas de copyright, aucun droits
*/
package uppa.project.database.pojo;
import jakarta.persistence.Transient;
/**
* Représentation d'une carte
*
* @author Kevin Mitressé
* @author Lucàs Vabre
*/
public class Card {
/**
* Couleurs disponibles pour les cartes
*/
public enum Color{HEART, CLUBS, SPADES, DIAMONDS}
/**
* Valeurs disponibles pour les cartes
*/
public enum Value{ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING}
/**
* Couleur de la carte
*/
private final Color color;
/**
* Valeur de la carte
*/
private final Value value;
/**
* Constructeur par défaut
*
* @param color couleur de la carte
* @param value valeur de la carte
*
* @see Color
* @see Value
*/
public Card(Color color, Value value) {
if (color == null || value == null) {
throw new IllegalArgumentException("Color cannot be null");
}
this.color = color;
this.value = value;
}
/**
* @return la couleur de la carte
*/
public Color getColor() {
return color;
}
/**
* @return la valeur de la carte
*/
public Value getValue() {
return value;
}
@Override
public String toString() {
return "Card{" +
"color=" + color +
", value=" + value +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Card card)) return false;
return getColor() == card.getColor() && getValue() == card.getValue();
}
}
@@ -0,0 +1,103 @@
/*
* Deck.java, 20/03/2024
* UPPA M1 TI 2023-2024
* Pas de copyright, aucun droits
*/
package uppa.project.database.pojo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Représentation d'un paquet de cartes
*
* @author Kevin Mitressé
* @author Lucàs Vabre
*/
public class Deck {
public static final int NB_COLORS_MIN = 2;
public static final int NB_COLORS_MAX = Card.Color.values().length;
public static final int NB_VALUES_PER_COLOR_MIN = 2;
public static final int NB_VALUES_PER_COLOR_MAX = Card.Value.values().length;
/**
* Ensemble de cartes du paquet
* @see Card
*/
private final List<Card> cards;
/**
* Constructeur par défaut
*
* @param nbColors nombre de couleurs (doit être compris entre 1 et le nombre de couleurs de {@link Card.Color})
* @param nbValues nombre de valeurs (doit être compris entre 1 et le nombre de valeurs de {@link Card.Value})
* @see Card.Color
* @see Card.Value
*/
public Deck(int nbColors, int nbValues) {
if (!Deck.isDeckValid(nbColors, nbValues)) {
throw new IllegalArgumentException("Nombre de couleurs et/ou nombre de valeurs invalide(s)");
}
this.cards = new ArrayList<>(nbColors * nbValues);
for (int nbCard = 0; nbCard < nbColors * nbValues; nbCard++) {
int color = nbCard % nbColors;
int value = nbCard / nbColors;
cards.add(new Card(Card.Color.values()[color], Card.Value.values()[value]));
}
}
/**
* Prédicat qui vérifie si un Deck est correctement initialisé
*
* @param nbColors Nombre de couleurs {@link Card.Color}
* @param nbValues Nombre de valeurs {@link Card.Value}
* @return true si le prédicat est vérifié, false sinon
*/
public static boolean isDeckValid(int nbColors, int nbValues) {
return NB_COLORS_MIN <= nbColors && nbColors <= NB_COLORS_MAX
&& NB_VALUES_PER_COLOR_MIN <= nbValues && nbValues <= NB_VALUES_PER_COLOR_MAX;
}
/**
* @return l'ensemble de cartes du paquet
*/
public List<Card> getCards() {
return cards;
}
/**
* Mélange les cartes d'un paquet
*/
public void shuffle() {
Collections.shuffle(this.cards);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Deck deck = (Deck) o;
boolean sameSize = cards.size() == deck.cards.size();
int counter = 0;
for (int i = 0; i < cards.size(); i++) {
for (int j = 0; j < deck.cards.size(); j++) {
if (cards.get(i).getColor() == deck.cards.get(j).getColor()
&& cards.get(i).getValue() == deck.cards.get(j).getValue()) {
counter++;
break;
}
}
}
return sameSize && counter == cards.size();
}
}
@@ -0,0 +1,372 @@
/*
* Game.java, 20/03/2024
* UPPA M1 TI 2023-2024
* Pas de copyright, aucun droits
*/
package uppa.project.database.pojo;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import jakarta.persistence.Transient;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* Représentation d'une partie de jeu
*
* @author Kevin Mitressé
* @author Lucàs Vabre
*/
@Entity
@Table(name = "game")
public class Game implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private BigDecimal id;
@Transient
private int currentRound = 0;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_at")
private Date createdAt;
@Column(name = "difficulty")
@Enumerated(EnumType.STRING)
private Difficulty difficulty;
@Column(name = "nb_rounds")
private int nbRounds;
@Column(name = "nb_colors")
private int nbColors;
@Column(name = "nb_values_per_color")
private int nbValuesPerColor;
@OneToMany(mappedBy = "game", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Player> players;
@Transient
private Deck deck;
@Column(name = "timer")
private int timer;
@Transient
public static final int TIMER_MIN = 5;
@Transient
public static final int TIMER_MAX = 60;
@Transient
public static final int NB_ROUNDS_MIN = 3;
@Transient
private GameState gameState = GameState.WAITING;
/**
* Constructeur par défaut
*/
public Game() {
this.createdAt = new Date();
this.players = new ArrayList<>();
this.timer = 0;
}
/**
* Constructeur avec la difficulté
*
* @param difficulty la difficulté de la partie
* @see Difficulty
* @param timer le timer de chaque round en seconde
* @param nbRounds le nombre de tours de la partie
* @param nbColors le nombre de couleurs présente dans le deck
* @param nbValuesPerColor le nombre de valeurs par couleur
*/
public Game(Difficulty difficulty, int timer, int nbRounds, int nbColors, int nbValuesPerColor) {
if (isValidNumberRound(nbRounds, nbColors, nbValuesPerColor)){
throw new IllegalArgumentException("Le nombre de tours doit être compris entre 2 et " + nbColors * nbValuesPerColor);
}
if (timer < TIMER_MIN || timer > TIMER_MAX){
throw new IllegalArgumentException("Le timer doit être compris entre" + TIMER_MIN + " et " + TIMER_MAX + " secondes");
}
this.difficulty = difficulty;
this.nbRounds = nbRounds;
this.nbColors = nbColors;
this.nbValuesPerColor = nbValuesPerColor;
this.deck = new Deck(nbColors, nbValuesPerColor);
this.deck.shuffle();
this.players = new ArrayList<>();
this.createdAt = new Date();
this.timer = timer;
}
/**
* Constructeur d'une partie de jeu
*
* @param id l'identifiant de la partie
* @param createdAt la date de création de la partie
* @param difficulty la difficulté de la partie
* @param nbRounds le nombre de tours de la partie
* @param nbColors le nombre de couleurs présente dans le deck
* @param nbValuesPerColor le nombre de valeurs par couleur
* @param players les joueurs de la partie
*/
public Game(BigDecimal id, Date createdAt, Difficulty difficulty, int timer ,int nbRounds, int nbColors, int nbValuesPerColor, ArrayList<Player> players) {
if (isValidNumberRound(nbRounds, nbColors, nbValuesPerColor)){
throw new IllegalArgumentException("Le nombre de tours doit être compris entre 1 et " + nbColors * nbValuesPerColor);
}
if (timer < TIMER_MIN || timer > TIMER_MAX){
throw new IllegalArgumentException("Le timer doit être compris entre" + TIMER_MIN + " et " + TIMER_MAX + " secondes");
}
this.id = id;
this.createdAt = createdAt;
this.difficulty = difficulty;
this.nbRounds = nbRounds;
this.nbColors = nbColors;
this.nbValuesPerColor = nbValuesPerColor;
this.players = players;
this.deck = new Deck(nbColors, nbValuesPerColor);
this.timer = timer;
}
@Override
public int hashCode() {
return Objects.hash(id, createdAt);
}
/**
* @return l'identifiant de la partie
*/
public BigDecimal getId() {
return id;
}
/**
* @return la date de création de la partie
*/
public Date getCreatedAt() {
return createdAt;
}
/**
* @return la difficulté de la partie
*/
public Difficulty getDifficulty() {
return difficulty;
}
/**
* @param difficulty la nouvelle difficulté de la partie
* @see Difficulty
*/
public void setDifficulty(Difficulty difficulty) {
this.difficulty = difficulty;
}
/**
* @return le nombre de tours de la partie
*/
public int getNbRounds() {
return nbRounds;
}
/**
* @param nbRounds le nouveau nombre de tours de la partie
*/
public void setNbRounds(int nbRounds) {
if (nbRounds < NB_ROUNDS_MIN || nbRounds > nbColors * nbValuesPerColor){
throw new IllegalArgumentException("Le nombre de tours doit être compris entre " + NB_ROUNDS_MIN + " et " + nbColors * nbValuesPerColor);
}
if (nbColors < Deck.NB_COLORS_MIN || nbColors > Deck.NB_COLORS_MAX) {
throw new IllegalArgumentException("Le nombre de couleurs doit être compris entre" + NB_ROUNDS_MIN + " et " + Deck.NB_COLORS_MAX);
}
if (nbValuesPerColor < Deck.NB_VALUES_PER_COLOR_MIN || nbValuesPerColor > Deck.NB_VALUES_PER_COLOR_MAX) {
throw new IllegalArgumentException("Le nombre de valeurs par couleur doit être compris entre " + Deck.NB_VALUES_PER_COLOR_MIN + " et " + Deck.NB_VALUES_PER_COLOR_MAX);
}
this.nbRounds = nbRounds;
}
/**
* @return le nombre de couleurs présente dans le deck
*/
public int getNbColors() {
return nbColors;
}
/**
* @param nbColors le nouveau nombre de couleurs présente dans le deck
*/
public void setNbColors(int nbColors) {
if (nbColors < Deck.NB_COLORS_MIN || nbColors > Deck.NB_COLORS_MAX) {
throw new IllegalArgumentException("Le nombre de couleurs doit être compris entre " + Deck.NB_COLORS_MIN + " et " + Deck.NB_COLORS_MAX);
}
this.nbColors = nbColors;
}
/**
* @return le nombre de valeurs par couleur
*/
public int getNbValuesPerColor() {
return nbValuesPerColor;
}
/**
* @param nbValuesPerColor le nouveau nombre de valeurs par couleur
*/
public void setNbValuesPerColor(int nbValuesPerColor) {
if (nbValuesPerColor < Deck.NB_VALUES_PER_COLOR_MIN || nbValuesPerColor > Deck.NB_VALUES_PER_COLOR_MAX) {
throw new IllegalArgumentException("Le nombre de valeurs par couleur doit être compris entre " + Deck.NB_VALUES_PER_COLOR_MIN + " et " + Deck.NB_VALUES_PER_COLOR_MAX);
}
this.nbValuesPerColor = nbValuesPerColor;
}
/**
* @return les joueurs de la partie
*/
public List<Player> getPlayers() {
return players;
}
/**
* Modifie les joueurs de la partie
*
* @param players les nouveaux joueurs
*/
public void setPlayers(List<Player> players) {
this.players = players;
}
/**
* @return le nombre de joueurs de la partie
*/
public int getNbPlayers() {
return this.players.size();
}
/**
* Ajoute un joueur à la partie
*
* @param player le joueur à ajouter
*/
public void addPlayer(Player player) {
this.players.add(player);
}
public int getTimer() {
return timer;
}
public void setTimer(int timer) {
this.timer = timer;
}
public Deck getDeck() {
if (deck == null) {
deck = new Deck(nbColors, nbValuesPerColor);
deck.shuffle();
}
return deck;
}
/**
* Tri des joueurs de la partie par score puis par rapidité
*/
public void sortPlayersByScoreAndRapidity() {
players.sort((p1, p2) -> {
if (p1.getScore() == p2.getScore()) {
return p2.getRapidClickCount() - p1.getRapidClickCount();
}
return p2.getScore() - p1.getScore();
});
}
/**
* Récupère le gagnant de la partie
*
* @return le nom du gagnant
*/
public Player getWinner(){
this.sortPlayersByScoreAndRapidity();
return players.get(0);
}
/**
* Vérifie si le nombre de tours est valide
*
* @param nbRounds le nombre de tours
* @param nbColors le nombre de couleurs
* @param nbValuesPerColor le nombre de valeurs par couleur
* @return true si le nombre de tours est valide, false sinon
*/
public boolean isValidNumberRound(int nbRounds, int nbColors, int nbValuesPerColor){
return nbRounds < NB_ROUNDS_MIN || nbRounds > nbColors * nbValuesPerColor;
}
@Override
public String toString() {
return String.format("Game{id=%s, createdAt=%s, difficulty=%s, nbRounds=%d, nbColors=%d, nbValuesPerColor=%d}",
id != null ? id.toString() : "null",
createdAt != null ? createdAt.toString() : "null",
difficulty != null ? difficulty.toString() : "null",
nbRounds != 0 ? nbRounds : 0,
nbColors != 0 ? nbColors : 0,
nbValuesPerColor != 0 ? nbValuesPerColor : 0
);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Game)) return false;
Game game = (Game) o;
return getNbRounds() == game.getNbRounds() && getNbColors() == game.getNbColors() && getNbValuesPerColor() == game.getNbValuesPerColor() && Objects.equals(getId(), game.getId()) && Objects.equals(getCreatedAt(), game.getCreatedAt()) && getDifficulty() == game.getDifficulty() && Objects.equals(getPlayers(), game.getPlayers()) && Objects.equals(getDeck(), game.getDeck());
}
/**
* Difficulté possible d'une partie
*/
public enum Difficulty {EASY, HARD}
public enum GameState {WAITING, STARTED, FINISHED}
public GameState getGameState() {
return gameState;
}
public void setGameState(GameState gameState) {
this.gameState = gameState;
}
public int getCurrentRound() {
return currentRound;
}
public boolean nextRound() {
currentRound++;
if (currentRound >= nbRounds) gameState = GameState.FINISHED;
return gameState != GameState.FINISHED;
}
}
@@ -0,0 +1,406 @@
/*
* Player.java, 20/03/2024
* UPPA M1 TI 2023-2024
* Pas de copyright, aucun droits
*/
package uppa.project.database.pojo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.websocket.Session;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Objects;
import uppa.project.json.websocket.ClickChoice;
/**
* Représentation d'un joueur
*
* @author Kevin Mitressé
* @author Lucàs Vabre
*/
@Entity
@Table(name = "player")
public class Player implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private BigDecimal id;
@ManyToOne
@JoinColumn(name = "game_id", nullable = false)
private Game game;
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private User user;
@Column(name = "score")
private int score;
@Column(name = "winner")
private boolean winner;
@Column(name = "click_count")
private int clickCount;
@Column(name = "right_click_count")
private int rightClickCount;
@Transient
private int tmpRightClickCount;
@Transient
private int partialRightClickCount;
@Transient
private int tmpPartialRightClickCount;
@Column(name = "rapid_click_count")
private int rapidClickCount;
@Transient
private Deck deck;
@Transient
private Session session = null;
@Transient
private ClickChoice currentClick = null;
/**
* Constructeur par défaut
*/
public Player() {
}
public Player(Game game, User user, Session session) {
this.game = game;
this.user = user;
this.session = session;
this.score = 0;
this.winner = false;
this.clickCount = 0;
this.rightClickCount = 0;
this.rapidClickCount = 0;
this.deck = new Deck(game.getNbColors(), game.getNbValuesPerColor());
this.deck.shuffle();
this.partialRightClickCount = 0;
this.tmpRightClickCount = 0;
this.tmpPartialRightClickCount = 0;
}
/**
* Constructeur d'un joueur
*
* @param game la partie de jeu
* @param user l'utilisateur
*/
public Player(Game game, User user) {
this.game = game;
this.user = user;
this.score = 0;
this.winner = false;
this.clickCount = 0;
this.rightClickCount = 0;
this.rapidClickCount = 0;
this.deck = new Deck(game.getNbColors(), game.getNbValuesPerColor());
this.deck.shuffle();
this.partialRightClickCount = 0;
this.tmpRightClickCount = 0;
this.tmpPartialRightClickCount = 0;
}
/**
* Constructeur d'un joueur
*
* @param game la partie de jeu
* @param user l'utilisateur
* @param score le score
* @param winner si le joueur est gagnant
* @param clickCount le nombre de clics
* @param rightClickCount le nombre de clics corrects
* @param rapidClickCount le nombre de clics rapides
*/
public Player(BigDecimal id, Game game, User user, int score, boolean winner, int clickCount, int rightClickCount, int rapidClickCount) {
this.id = id;
this.game = game;
this.user = user;
this.score = score;
this.winner = winner;
this.clickCount = clickCount;
this.rightClickCount = rightClickCount;
this.rapidClickCount = rapidClickCount;
this.partialRightClickCount = 0;
this.tmpRightClickCount = 0;
this.tmpPartialRightClickCount = 0;
this.deck = new Deck(game.getNbColors(), game.getNbValuesPerColor());
this.deck.shuffle();
}
@Override
public int hashCode() {
return Objects.hash(game, user, score, winner, clickCount, rightClickCount, rapidClickCount);
}
/**
* @return La partie de jeu du joueur
*/
public Game getGame() {
return game;
}
/**
* Modifie la partie de jeu du joueur
*
* @param game le nouveau jeu
*/
public void setGame(Game game) {
this.game = game;
}
/**
* @return l'utilisateur
*/
public User getUser() {
return user;
}
/**
* Modifie l'utilisateur
*
* @param user le nouvel utilisateur
*/
public void setUser(User user) {
this.user = user;
}
/**
* @return l'identifiant
*/
public int getScore() {
return score;
}
/**
* Modifie le score
*
* @param score le nouveau score
*/
public void setScore(int score) {
this.score = score;
}
/**
* Ajoute des points au score
*
* @param points les points à ajouter
*/
public void addToScore(int points) {
this.score += points;
}
/**
* @return le score maximum possible
*/
public int getScoreMax(){
return game.getNbRounds() * 3; // 2 points par bonne réponse + 1 point pour le clic rapide
}
/**
* @return si le joueur est gagnant
*/
public boolean isWinner() {
return winner;
}
/**
* Déclare le joueur comme gagnant
*/
public void setWinner() {
this.winner = true;
}
/**
* @return le nombre de clics
*/
public int getClickCount() {
return clickCount;
}
/**
* Modifie le nombre de clics
*
* @param clickCount le nouveau nombre de clics
*/
public void setClickCount(int clickCount) {
this.clickCount = clickCount;
}
/**
* Incrémente de 1 le nombre de clics
*/
public void incrementClickCount() {
clickCount++;
}
/**
* @return le nombre de clics corrects
*/
public int getRightClickCount() {
return rightClickCount;
}
/**
* Modifie le nombre de clics corrects
*
* @param rightClickCount le nouveau nombre de clics corrects
*/
public void setRightClickCount(int rightClickCount) {
this.rightClickCount = rightClickCount;
}
/**
* Incrémente de 1 le nombre de clics corrects
*/
public void incrementRightClickCount() {
rightClickCount++;
}
/**
* @return le nombre de clics corrects du joueur
*/
public int getTmpRightClickCount() {
return tmpRightClickCount;
}
/**
* Incrémente de 1 le nombre de clics corrects
*/
public void incrementTmpRightClickCount() {
tmpRightClickCount++;
}
/**
* @return le pourcentage de clics corrects du joueur sur la partie courante
*/
public double getRatioRightClick() {
if (clickCount == 0 || rightClickCount == 0) return 0;
return (double) Math.abs(rightClickCount * 10000 / game.getNbRounds()) / 100;
}
/**
* @return le nombre de clics rapides
*/
public int getRapidClickCount() {
return rapidClickCount;
}
/**
* @return le nombre de clics partiels corrects
*/
public int getPartialRightClickCount() {
return partialRightClickCount;
}
/**
* Incrémente de 1 le nombre de clics partielement corrects
*/
public void incrementPartialRightClickCount() {
partialRightClickCount++;
}
/**
* @return le nombre de clics corrects du joueur
*/
public int getTmpPartialRightClickCount() {
return tmpPartialRightClickCount;
}
/**
* Incrémente de 1 le nombre de clics corrects
*/
public void incrementTmpPartialRightClickCount() {
tmpPartialRightClickCount++;
}
/**
* Modifie le nombre de clics rapides
*
* @param rapidClickCount le nouveau nombre de clics rapides
*/
public void setRapidClickCount(int rapidClickCount) {
this.rapidClickCount = rapidClickCount;
}
/**
* Incrémente de 1 le nombre de clics rapides
*/
public void incrementRapidClickCount() {
rapidClickCount++;
}
/**
* @return le pourcentage de clics rapides du joueur sur la partie courante
*/
public double getRatioRapidClick() {
if (clickCount == 0 || rapidClickCount == 0) return 0;
return (double) Math.abs(rapidClickCount * 10000 / game.getNbRounds()) / 100;
}
public Deck getDeck() {
return deck;
}
@Override
public String toString() {
return String.format("Player{id=%s, game=%s, user=%s, score=%d, winner=%b, clickCount=%d, rightClickCount=%d, rapidClickCount=%d}",
id != null ? id.toString() : "null", game != null ? game.toString() : "null", user != null ? user.toString() : "null", score,
winner, clickCount, rightClickCount, rapidClickCount);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Player)) return false;
Player player = (Player) o;
return getScore() == player.getScore() && isWinner() == player.isWinner() && getClickCount() == player.getClickCount() && getRightClickCount() == player.getRightClickCount() && getRapidClickCount() == player.getRapidClickCount() && Objects.equals(id, player.id) && Objects.equals(getGame(), player.getGame()) && Objects.equals(getUser(), player.getUser());
}
/**
* @return l'identifiant
*/
public BigDecimal getId() {
return id;
}
public Session getSession() {
return session;
}
public ClickChoice getCurrentClick() {
return currentClick;
}
public void setCurrentClick(ClickChoice currentClick) {
this.currentClick = currentClick;
}
public void setPartialRightClickCount(int tmpPartialRightClickCount) {
this.partialRightClickCount = tmpPartialRightClickCount;
}
}
@@ -0,0 +1,165 @@
package uppa.project.database.pojo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;
/**
* Représentation d'un token de réinitialisation de mot de passe
*
* @author Kevin Mitressé
* @author Lucàs Vabre
*/
@Entity
@Table(name = "recovery_password_token")
public class RecoveryPasswordToken {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private BigDecimal id;
@Column(name = "token")
private String token;
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private User user;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "expires_at")
private Date expiresAt;
public RecoveryPasswordToken() {
}
/**
* Constructeur
*
* @param token
* @param user
*/
public RecoveryPasswordToken(String token, User user) {
this.token = token;
this.user = user;
this.expiresAt = new Date(new Date().getTime() + 600000);
}
/**
* Constructeur depuis la base de données
*
* @param id
* @param token
* @param user
*/
public RecoveryPasswordToken(BigDecimal id, String token, User user, Date expiresAt) {
this.id = id;
this.token = token;
this.user = user;
this.expiresAt = expiresAt;
}
/**
* Récupère l'id de l'instance
*
* @return l'id
*/
public BigDecimal getId() {
return id;
}
/**
* Récupère le token
*
* @return le token
*/
public String getToken() {
return token;
}
/**
* Définit le token
*
* @param token
*/
public void setToken(String token) {
this.token = token;
}
/**
* Récupère l'utilisateur associé au token
*
* @return l'utilisateur associé au token
*/
public User getUser() {
return user;
}
/**
* Définit l'utilisateur associé au token
*
* @param user
*/
public void setUser(User user) {
this.user = user;
}
/**
* Récupère la date d'expiration du token
*
* @return la date d'expiration du token
*/
public Date getExpiresAt() {
return expiresAt;
}
/**
* Définit la date d'expiration du token
*
* @param expiresAt
*/
public void setExpiresAt(Date expiresAt) {
this.expiresAt = expiresAt;
}
@Override
public int hashCode() {
return Objects.hash(getId(), getToken(), getUser(), getExpiresAt());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof RecoveryPasswordToken)) return false;
RecoveryPasswordToken that = (RecoveryPasswordToken) o;
return Objects.equals(getId(), that.getId()) && Objects.equals(getToken(), that.getToken()) && Objects.equals(getUser(), that.getUser()) && Objects.equals(getExpiresAt(), that.getExpiresAt());
}
@Override
public String toString() {
return String.format("RecoveryPasswordToken{id=%s, token=%s, user=%s, expiresAt=%s}",
id != null ? id.toString() : "null",
token != null ? token : "null",
user != null ? user.toString() : "null",
expiresAt != null ? expiresAt.toString() : "null");
}
/**
* Récupère la date d'expiration du token
*
* @return la date d'expiration du token
*/
public Date getExpirationDate() {
return expiresAt;
}
}
@@ -0,0 +1,472 @@
/*
* User.java, 20/03/2024
* UPPA M1 TI 2023-2024
* Pas de copyright, aucun droits
*/
package uppa.project.database.pojo;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import jakarta.persistence.Transient;
import java.io.Serializable;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* Représentation d'un utilisateur
*
* @author Kevin Mitressé
* @author Lucàs Vabre
*/
@Entity
@Table(name = "user")
public class User implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private BigDecimal id;
@Column(name = "username")
private String username;
@Column(name = "email")
private String email;
@Column(name = "password")
private String password;
@Temporal(TemporalType.DATE)
@Column(name = "birth")
private Date birth;
@Column(name = "gender")
@Enumerated(EnumType.STRING)
private Gender gender;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Player> playedGames;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<RecoveryPasswordToken> recoveryPasswordTokens;
@Transient
private int nbPlayedGame;
@Transient
private int nbWin;
@Transient
private double winRate;
@Transient
private int nbClicks;
@Transient
private int nbRightClicks;
@Transient
private double rightClickPercentRate;
@Transient
private int nbRapidClicks;
@Transient
private double rapidClickPercentRate;
/**
* Constructeur par défaut
*/
public User() {
}
/**
* Constructeur d'un utilisateur
*
* @param username le pseudonyme
* @param email l'adresse email
* @param password le mot de passe
* @param birth la date de naissance
* @param gender le genre
*/
public User(String username, String email, String password, Date birth, Gender gender) {
if (!isValidBirthDate(birth)){
throw new IllegalArgumentException("La date de naissance n'est pas valide");
}
this.username = username;
this.email = email;
this.password = hashPassword(password);
this.birth = birth;
this.gender = gender;
this.playedGames = new ArrayList<>();
this.recoveryPasswordTokens = new ArrayList<>();
this.nbPlayedGame = 0;
this.nbWin = 0;
this.winRate = 0;
this.nbClicks = 0;
this.nbRightClicks = 0;
this.rightClickPercentRate = 0;
this.nbRapidClicks = 0;
this.rapidClickPercentRate = 0;
}
public User(BigDecimal id, String username, String email, String password, Date birth, Gender gender, List<Player> playedGames) {
if (!isValidBirthDate(birth)){
throw new IllegalArgumentException("La date de naissance n'est pas valide");
}
this.id = id;
this.username = username;
this.email = email;
this.password = password;
this.birth = birth;
this.gender = gender;
this.playedGames = playedGames;
this.nbPlayedGame = playedGames.size();
this.nbWin = getNbWin();
this.winRate = getWinRate();
this.nbClicks = getNbClicks();
this.nbRightClicks = getNbRightClicks();
this.rightClickPercentRate = getRightClickPercentRate();
this.nbRapidClicks = getNbRapidClicks();
this.rapidClickPercentRate = getRapidClickPercentRate();
}
/**
* Constructeur d'un utilisateur
*
* @param id l'identifiant
* @param username le pseudonyme
* @param email l'adresse email
* @param password le mot de passe
* @param birth la date de naissance
* @param gender le genre
*/
public User(BigDecimal id, String username, String email, String password, Date birth, Gender gender, List<Player> playedGames, List<RecoveryPasswordToken> recoveryPasswordToken) {
if (!isValidBirthDate(birth)){
throw new IllegalArgumentException("La date de naissance n'est pas valide");
}
this.id = id;
this.username = username;
this.email = email;
this.password = password;
this.birth = birth;
this.gender = gender;
this.playedGames = playedGames;
this.recoveryPasswordTokens = recoveryPasswordToken;
this.nbPlayedGame = playedGames.size();
this.nbWin = getNbWin();
this.winRate = getWinRate();
this.nbClicks = getNbClicks();
this.nbRightClicks = getNbRightClicks();
this.rightClickPercentRate = getRightClickPercentRate();
this.nbRapidClicks = getNbRapidClicks();
this.rapidClickPercentRate = getRapidClickPercentRate();
}
/**
* Hash le mot de passe en SHA-256
*
* @param password le mot de passe à hasher
* @return le mot de passe hashé
*/
public static String hashPassword(String password) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] encodedhash = digest.digest(password.getBytes());
StringBuilder hexString = new StringBuilder();
for (byte b : encodedhash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
@Override
public int hashCode() {
return Objects.hash(id, username, email, password, birth, gender);
}
/**
* @return l'identifiant de l'utilisateur
*/
public BigDecimal getId() {
return id;
}
/**
* @return le pseudonyme de l'utilisateur
*/
public String getUsername() {
return username;
}
/**
* Modifie le pseudonyme de l'utilisateur
*
* @param username le nouveau pseudonyme
*/
public void setUsername(String username) {
this.username = username;
}
/**
* @return l'adresse email de l'utilisateur
*/
public String getEmail() {
return email;
}
/**
* Modifie l'adresse email de l'utilisateur
*
* @param email la nouvelle adresse email
*/
public void setEmail(String email) {
this.email = email;
}
/**
* @return le mot de passe hashé de l'utilisateur
*/
public String getPassword() {
return password;
}
/**
* Modifie le mot de passe de l'utilisateur
* Le mot de passe est hashé avant d'être stocké
*
* @param password le nouveau mot de passe
*/
public void setPassword(String password) {
this.password = hashPassword(password);
}
/**
* @return la date de naissance de l'utilisateur
*/
public Date getBirth() {
return birth;
}
/**
* Modifie la date de naissance de l'utilisateur
*
* @param birth la nouvelle date de naissance
*/
public void setBirth(Date birth) {
this.birth = birth;
}
/**
* @return le genre de l'utilisateur
*/
public Gender getGender() {
return gender;
}
/**
* Modifie le genre de l'utilisateur
*/
public void setGender(Gender gender) {
this.gender = gender;
}
/**
* Prédicat qui vérifie si le mot de passe fourni est correct
*
* @param password le mot de passe à vérifier
* @return true si le prédicat est vérifié, false sinon
*/
public boolean verifyPassword(String password) {
String hashedPassword = hashPassword(password);
return hashedPassword != null && hashedPassword.equals(this.password);
}
/**
* Récupère la liste des parties jouées par l'utilisateur
*
* @return la liste des parties jouées
*/
public List<Player> getPlayedGames() {
return playedGames;
}
/**
* Ajoute une partie dans la liste des parties jouées
*
* @param player la nouvelle partie jouée
*/
public void addPlayedGame(Player player){
playedGames.add(player);
}
/**
* Récupère le nombre de parties jouées
*
* @return le nombre de parties jouées
*/
public int getNbPlayedGame() {
return playedGames.size();
}
/**
* Récupère le nombre de parties gagnées
*
* @return le nombre de parties gagnées
*/
public int getNbWin(){
int nbWin = 0;
for (Player p : playedGames) {
if (p.isWinner()) nbWin++;
}
return nbWin;
}
public double getScoreRate(){
if (getNbPlayedGame() == 0) return 0;
int maxScore = 0;
int totalScore = 0;
for (Player p : playedGames) {
maxScore += p.getScoreMax();
totalScore += p.getScore();
}
return (double) Math.abs(totalScore * 100 / maxScore);
}
/**
* Récupère le pourcentage de victoire
*
* @return le pourcentage de victoire
*/
public double getWinRate(){
if (getNbPlayedGame() == 0 || getNbWin() == 0) return 0;
return (double) Math.abs(getNbWin() * 10000 / getNbPlayedGame()) /100;
}
/**
* Récupère le nombre total de clics toute partie confondue
*
* @return le nombre total de clics
*/
public int getNbClicks(){
int nbClicks = 0;
for (Player p : playedGames) {
nbClicks += p.getClickCount();
}
return nbClicks;
}
/**
* Récupère le nombre total de clics réussi toute partie confondue
*
* @return le nombre total de clics réussi
*/
public int getNbRightClicks(){
int nbRightClicks = 0;
for (Player p : playedGames) {
nbRightClicks += p.getRightClickCount();
}
return nbRightClicks;
}
/**
* Récupère le pourcentage de clics réussi
*
* @return le pourcentage de clics réussi
*/
public double getRightClickPercentRate(){
if (getNbClicks() == 0 || getNbRightClicks() == 0) return 0;
return (double) Math.abs(getNbRightClicks() * 10000 / getNbClicks())/100;
}
/**
* Récupère le nombre total de clics les plus rapides toute partie confondue
*
* @return le nombre total de clics les plus rapides
*/
public int getNbRapidClicks(){
int nbRapidClicks = 0;
for (Player p : playedGames) {
nbRapidClicks += p.getRapidClickCount();
}
return nbRapidClicks;
}
/**
* Récupère le pourcentage de clics les plus rapides
*
* @return le pourcentage de clics les plus rapides
*/
public double getRapidClickPercentRate(){
if (getNbClicks() == 0 || getNbRapidClicks() == 0) return 0;
return (double) Math.abs(getNbRapidClicks() * 10000 / getNbClicks())/100;
}
public boolean isValidBirthDate(Date birthdate){
Date currentDate = new Date();
return birthdate.before(currentDate) || birthdate.equals(currentDate);
}
@Override
public String toString() {
return String.format(
"User{id='%s', username=%s, email=%s, birth='%s', gender='%s'}",
id != null ? id.toString() : "null",
username != null ? username : "null",
email != null ? email : "null",
birth != null ? birth.toString() : "null",
gender != null ? gender.toString() : "null"
);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return Objects.equals(id, user.id)
&& Objects.equals(username, user.username)
&& Objects.equals(email, user.email)
&& Objects.equals(password, user.password)
&& Objects.equals(birth, user.birth)
&& gender == user.gender
&& Objects.equals(playedGames, user.playedGames)
&& Objects.equals(recoveryPasswordTokens, user.recoveryPasswordTokens);
}
/**
* Enumération des genres possibles
*/
public enum Gender {MALE, FEMALE, OTHER}
public static Gender getGender(String value) throws IllegalArgumentException{
if (value.equals("MALE")) return Gender.MALE;
if (value.equals("FEMALE")) return Gender.FEMALE;
if (value.equals("OTHER")) return Gender.OTHER;
throw new IllegalArgumentException("Le genre selectionné n'existe pas");
}
}